我正在开发一个使用SQLite的应用程序.我想使用分页机制显示用户列表(UITableView).任何人都可以告诉我当用户滚动到列表末尾时如何在我的列表中加载更多数据(比如在Facebook应用程序的主页上)?
您可以通过添加检查cellForRowAtIndexPath:
方法中的位置来实现.这种方法易于理解和实现:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { // Classic start method static NSString *cellIdentifier = @"MyCell"; MyCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; if (!cell) { cell = [[MyCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MainMenuCellIdentifier]; } MyData *data = [self.dataArray objectAtIndex:indexPath.row]; // Do your cell customisation // cell.titleLabel.text = data.title; BOOL lastItemReached = [data isEqual:[[self.dataArray] lastObject]]; if (!lastItemReached && indexPath.row == [self.dataArray count] - 1) { [self launchReload]; } }
编辑:添加了对最后一项的检查以防止递归调用.您必须实现定义是否已到达最后一个项目的方法.
EDIT2:解释了lastItemReached
方法1:滚动到底部
这是Swro版本的PedroRomão的答案.当用户停止滚动时,它会检查它是否已到达底部.
func scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool) { // UITableView only moves in one direction, y axis let currentOffset = scrollView.contentOffset.y let maximumOffset = scrollView.contentSize.height - scrollView.frame.size.height // Change 10.0 to adjust the distance from bottom if maximumOffset - currentOffset <= 10.0 { self.loadMore() } }
方法2:到达最后一行
这是swinguX的答案的Swift版本.它检查用户是否已到达最后一行.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { // set up cell // ... // Check if the last row number is the same as the last current data element if indexPath.row == self.dataArray.count - 1 { self.loadMore() } }
loadMore()
方法示例我设置了这三个类变量来获取批量数据.
// number of items to be fetched each time (i.e., database LIMIT) let itemsPerBatch = 50 // Where to start fetching items (database OFFSET) var offset = 0 // a flag for when all database items have already been loaded var reachedEndOfItems = false
这是将更多项目从数据库加载到表视图中的功能.
func loadMore() { // don't bother doing another db query if already have everything guard !self.reachedEndOfItems else { return } // query the db on a background thread DispatchQueue.global(qos: .background).async { // determine the range of data items to fetch var thisBatchOfItems: [MyObjects]? let start = self.offset let end = self.offset + self.itemsPerBatch // query the database do { // SQLite.swift wrapper thisBatchOfItems = try MyDataHelper.findRange(start..<end) } catch _ { print("query failed") } // update UITableView with new batch of items on main thread after query finishes DispatchQueue.main.async { if let newItems = thisBatchOfItems { // append the new items to the data source for the table view self.myObjectArray.appendContentsOf(newItems) // reload the table view self.tableView.reloadData() // check if this was the last of the data if newItems.count < self.itemsPerBatch { self.reachedEndOfItems = true print("reached end of data. Batch count: \(newItems.count)") } // reset the offset for the next data query self.offset += self.itemsPerBatch } } } }
最好使用willDisplayCell
方法来检查是否将加载哪个单元格.一旦我们得到indexPath.row
最后的电流,我们可以加载更多的单元格.这将在向下滚动时加载更多单元格.
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { // check if indexPath.row is last row // Perform operation to load new Cell's. }
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { NSInteger lastSectionIndex = [tableView numberOfSections] - 1; NSInteger lastRowIndex = [tableView numberOfRowsInSection:lastSectionIndex] - 1; if ((indexPath.section == lastSectionIndex) && (indexPath.row == lastRowIndex)) { // This is the last cell [self loadMore]; } }
如果您使用的是Core Data NSFetchedResultsController
,则loadMore
可能如下所示:
// Load more - (void)loadMore { [self.fetchedResultsController.fetchRequest setFetchLimit:newFetchLimit]; [NSFetchedResultsController deleteCacheWithName:@"cache name"]; NSError *error; if (![self.fetchedResultsController performFetch:&error]) { // Update to handle the error appropriately. NSLog(@"Unresolved error %@, %@", error, [error userInfo]); } [self.tableView reloadData]; }
Xcode 10,Swift 4.2
使用UIScrollView/UICollectionView/UITableView
import UIKit protocol LoadMoreControlDelegate: class { func loadMoreControl(didStartAnimating loadMoreControl: LoadMoreControl) func loadMoreControl(didStopAnimating loadMoreControl: LoadMoreControl) } class LoadMoreControl { private let spacingFromLastCell: CGFloat private let indicatorHeight: CGFloat private weak var activityIndicatorView: UIActivityIndicatorView? private weak var scrollView: UIScrollView? weak var delegate: LoadMoreControlDelegate? private var defaultY: CGFloat { guard let height = scrollView?.contentSize.height else { return 0.0 } return height + spacingFromLastCell } init (scrollView: UIScrollView, spacingFromLastCell: CGFloat, indicatorHeight: CGFloat) { self.scrollView = scrollView self.spacingFromLastCell = spacingFromLastCell self.indicatorHeight = indicatorHeight let size:CGFloat = 40 let frame = CGRect(x: (scrollView.frame.width-size)/2, y: scrollView.contentSize.height + spacingFromLastCell, width: size, height: size) let activityIndicatorView = UIActivityIndicatorView(frame: frame) activityIndicatorView.color = .black activityIndicatorView.hidesWhenStopped = true activityIndicatorView.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin] scrollView.addSubview(activityIndicatorView) activityIndicatorView.isHidden = isHidden self.activityIndicatorView = activityIndicatorView } private var isHidden: Bool { guard let scrollView = scrollView else { return true } return scrollView.contentSize.height < scrollView.frame.size.height } func didScroll() { guard let scrollView = scrollView, let activityIndicatorView = activityIndicatorView else { return } let offsetY = scrollView.contentOffset.y activityIndicatorView.isHidden = isHidden if !activityIndicatorView.isHidden && offsetY >= 0 { let contentDelta = scrollView.contentSize.height - scrollView.frame.size.height let offsetDelta = offsetY - contentDelta let newY = defaultY-offsetDelta if newY < scrollView.frame.height { activityIndicatorView.frame.origin.y = newY } else { if activityIndicatorView.frame.origin.y != defaultY { activityIndicatorView.frame.origin.y = defaultY } } if !activityIndicatorView.isAnimating { if offsetY > contentDelta && offsetDelta >= indicatorHeight && !activityIndicatorView.isAnimating { activityIndicatorView.startAnimating() delegate?.loadMoreControl(didStartAnimating: self) } } if scrollView.isDecelerating { if activityIndicatorView.isAnimating && scrollView.contentInset.bottom == 0 { UIView.animate(withDuration: 0.3) { [weak self, weak scrollView] in if let bottom = self?.indicatorHeight { scrollView?.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: bottom, right: 0) } } } } } } func stop() { guard let scrollView = scrollView else { return } let contentDelta = scrollView.contentSize.height - scrollView.frame.size.height let offsetDelta = scrollView.contentOffset.y - contentDelta if offsetDelta >= 0 { UIView.animate(withDuration: 0.3, animations: { [weak scrollView] in scrollView?.contentInset = .zero }) { [weak self] result in if result { self?.endAnimating() } } } else { scrollView.contentInset = .zero endAnimating() } } private func endAnimating() { activityIndicatorView?.stopAnimating() delegate?.loadMoreControl(didStopAnimating: self) } }
loadMoreControl = LoadMoreControl(scrollView: tableView, spacingFromLastCell: 10, indicatorHeight: 60) loadMoreControl.delegate = self
extension ViewController: LoadMoreControlDelegate { func loadMoreControl(didStartAnimating loadMoreControl: LoadMoreControl) { print("didStartAnimating") } func loadMoreControl(didStopAnimating loadMoreControl: LoadMoreControl) { print("didStopAnimating") } } extension ViewController: UITableViewDelegate { func scrollViewDidScroll(_ scrollView: UIScrollView) { loadMoreControl.didScroll() } }
下面的链接将提供示例代码.#Swift3
用户需要拉出最后一个表格视图单元格,至少需要2个单元格才能从服务器获取更多数据.
您还会发现Process cell也显示加载过程,如同最后一个单元格一样.
它在Swift3中
https://github.com/yogendrabagoriya/YBTableViewPullData