如何检测UITableView的加载结束

我想在加载完成时更改表的偏移量,并且偏移量取决于表中加载的单元格的数量。

是否有任何方式在SDK上知道什么时候uitableview加载完成? 我在代表和数据源协议上都没有看到任何东西。

我不能使用数据源的数量,因为只能加载可见的单元格。

改进@RichX答案: lastRow可以是[tableView numberOfRowsInSection: 0] - 1((NSIndexPath*)[[tableView indexPathsForVisibleRows] lastObject]).row 。 所以代码将是:

 -(void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { if([indexPath row] == ((NSIndexPath*)[[tableView indexPathsForVisibleRows] lastObject]).row){ //end of loading //for example [activityIndicator stopAnimating]; } } 

更新:那么,@ htafoya的评论是正确的。 如果你想要这个代码来检测从源加载所有数据的结束,它不会,但这不是原来的问题。 此代码用于检测何时显示所有可见的单元格。 willDisplayCell:在这里使用更平滑的UI(单个单元通常在willDisplay: call之后快速显示)。 你也可以用tableView:didEndDisplayingCell:

我总是用这个非常简单的解决scheme:

 -(void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { if([indexPath row] == lastRow){ //end of loading //for example [activityIndicator stopAnimating]; } } 

Swift 2解决scheme:

 // willDisplay function override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) { let lastRowIndex = tableView.numberOfRowsInSection(0) if indexPath.row == lastRowIndex - 1 { fetchNewDataFromServer() } } // data fetcher function func fetchNewDataFromServer() { if(!loading && !allDataFetched) { // call beginUpdates before multiple rows insert operation tableView.beginUpdates() // for loop // insertRowsAtIndexPaths tableView.endUpdates() } } 

这是另一个似乎为我工作的选项。 在viewForFooter委托方法中检查它是否是最后一部分,并在那里添加你的代码。 在意识到willDisplayCell不包含页脚的情况下,这个方法就浮现出来了。

 - (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section { // Perform some final layout updates if (section == ([tableView numberOfSections] - 1)) { [self tableViewWillFinishLoading:tableView]; } // Return nil, or whatever view you were going to return for the footer return nil; } - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section { // Return 0, or the height for your footer view return 0.0; } - (void)tableViewWillFinishLoading:(UITableView *)tableView { NSLog(@"finished loading"); } 

我发现这种方法最好,如果你正在寻找整个UITableView的结束加载,而不是简单的可见单元格。 根据您的需要,您可能只需要可见的单元格,在这种情况下, folex的答案是一个好的途径。

试试这个魔法:

 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { // cancel the perform request if there is another section [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(tableViewDidLoadRows:) object:tableView]; // create a perform request to call the didLoadRows method on the next event loop. [self performSelector:@selector(tableViewDidLoadRows:) withObject:tableView afterDelay:0]; return self.objects.count; } // called after the rows in the last section is loaded -(void)tableViewDidLoadRows:(UITableView*)tableView{ // make the cell selected after all rows loaded if(self.selectedObject){ NSInteger index = [self.objects indexOfObject:self.selectedObject]; [tableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0] animated:NO scrollPosition:UITableViewScrollPositionMiddle]; } } 

表加载的行为意味着你不能调用select行,直到表知道行数,我想默认select一行。 我有一个表视图委托不是一个视图控制器,所以我不能简单地把表格单元格select在视图中出现或加载委托方法,没有其他答案是我喜欢的。

我知道的最好的方法是Eric的答案: 在UITableView完成数据请求时得到通知?

更新:为了使其工作,我必须把这些调用在-tableView:cellForRowAtIndexPath:

 [tableView beginUpdates]; [tableView endUpdates]; 

对于Swift 3中select的答案版本:

 var isLoadingTableView = true func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { if tableData.count > 0 && isLoadingTableView { if let indexPathsForVisibleRows = tableView.indexPathsForVisibleRows, let lastIndexPath = indexPathsForVisibleRows.last, lastIndexPath.row == indexPath.row { isLoadingTableView = false //do something after table is done loading } } } 

我需要isLoadingTableViewvariables,因为我想确保在进行默认单元格select之前完成了表的加载。 如果你不包括这个,那么每次你滚动表格时都会再次调用你的代码。

Swift 3版本:

 func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { if let lastVisibleIndexPath = tableView.indexPathsForVisibleRows?.last { if indexPath == lastVisibleIndexPath { // do here... } } } 

@folex答案是正确的。

但是如果tableView 一次显示多个部分 ,将会失败

 -(void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { if([indexPath isEqual:((NSIndexPath*)[[tableView indexPathsForVisibleRows] lastObject])]){ //end of loading } } 

在Swift中,你可以做这样的事情。 每当你到达tableView的结尾时,下面的条件都是真的

 func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) { if indexPath.row+1 == postArray.count { println("came to last row") } } 

我知道这是回答,我只是添加一个build议。

根据以下文件

https://www.objc.io/issues/2-concurrency/thread-safe-class-design/

修复dispatch_async的时间问题是一个坏主意。 我build议我们应该通过添加FLAG或其他东西来处理这个问题。

下面是你在Swift 3中的做法:

 override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { if indexPath.row == 0 { // perform your logic here, for the first row in the table } // .... } 

这里是我如何在Swift 3中完成的

 let threshold: CGFloat = 76.0 // threshold from bottom of tableView internal func scrollViewDidScroll(_ scrollView: UIScrollView) { let contentOffset = scrollView.contentOffset.y let maximumOffset = scrollView.contentSize.height - scrollView.frame.size.height; if (!isLoadingMore) && (maximumOffset - contentOffset <= threshold) { self.loadVideosList() } } 

如果你有多个部分,下面是如何得到最后一部分的最后一行(Swift 3):

 func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { if let visibleRows = tableView.indexPathsForVisibleRows, let lastRow = visibleRows.last?.row, let lastSection = visibleRows.map({$0.section}).last { if indexPath.row == lastRow && indexPath.section == lastSection { // Finished loading visible rows } } } 

这是我会做的。

  1. 在您的基类(可以是rootVC BaseVc等),

    A.写一个协议发送“DidFinishReloading”callback。

     @protocol ReloadComplition <NSObject> @required - (void)didEndReloading:(UITableView *)tableView; @end 

    B.编写一个通用的方法来重新加载表视图。

     -(void)reloadTableView:(UITableView *)tableView withOwner:(UIViewController *)aViewController; 
  2. 在基类方法实现中,延迟调用reloadData,然后调用delegateMethod。

     -(void)reloadTableView:(UITableView *)tableView withOwner:(UIViewController *)aViewController{ [[NSOperationQueue mainQueue] addOperationWithBlock:^{ [tableView reloadData]; if(aViewController && [aViewController respondsToSelector:@selector(didEndReloading:)]){ [aViewController performSelector:@selector(didEndReloading:) withObject:tableView afterDelay:0]; } }]; } 
  3. 在所有需要callback的视图控制器中确认重新加载完成协议。

     -(void)didEndReloading:(UITableView *)tableView{ //do your stuff. } 

参考: https : //discussions.apple.com/thread/2598339?start=0&tstart=0

您是否正在查找将在表格中显示的项目总数或当前可见项目的总数? 无论哪种方式..我相信,所有的数据源方法被调用后,“viewDidLoad”方法执行。 但是,这只会在数据的第一次加载(如果你使用一个单独的ViewController)。

我在复制安德鲁的代码,并将其扩大到在表中只有一行的情况。 它为我工作到目前为止!

 - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { // detect when all visible cells have been loaded and displayed // NOTE: iOS7 workaround used - see: http://stackoverflow.com/questions/4163579/how-to-detect-the-end-of-loading-of-uitableview?lq=1 NSArray *visibleRows = [tableView indexPathsForVisibleRows]; NSIndexPath *lastVisibleCellIndexPath = [visibleRows lastObject]; BOOL isPreviousCallForPreviousCell = self.previousDisplayedIndexPath.row + 1 == lastVisibleCellIndexPath.row; BOOL isLastCell = [indexPath isEqual:lastVisibleCellIndexPath]; BOOL isFinishedLoadingTableView = isLastCell && ([tableView numberOfRowsInSection:0] == 1 || isPreviousCallForPreviousCell); self.previousDisplayedIndexPath = indexPath; if (isFinishedLoadingTableView) { [self hideSpinner]; } } 

注意:我只是使用Andrew的代码中的1个部分,所以请牢记这一点。

在iOS7.0x中,解决scheme有点不同。 这是我想出来的。

  - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { BOOL isFinishedLoadingTableView = [self isFinishedLoadingTableView:tableView indexPath:indexPath]; if (isFinishedLoadingTableView) { NSLog(@"end loading"); } } - (BOOL)isFinishedLoadingTableView:(UITableView *)tableView indexPath:(NSIndexPath *)indexPath { // The reason we cannot just look for the last row is because // in iOS7.0x the last row is updated before // looping through all the visible rows in ascending order // including the last row again. Strange but true. NSArray * visibleRows = [tableView indexPathsForVisibleRows]; // did verify sorted ascending via logging NSIndexPath *lastVisibleCellIndexPath = [visibleRows lastObject]; // For tableviews with multiple sections this will be more complicated. BOOL isPreviousCallForPreviousCell = self.previousDisplayedIndexPath.row + 1 == lastVisibleCellIndexPath.row; BOOL isLastCell = [indexPath isEqual:lastVisibleCellIndexPath]; BOOL isFinishedLoadingTableView = isLastCell && isPreviousCallForPreviousCell; self.previousDisplayedIndexPath = indexPath; return isFinishedLoadingTableView; } 

目标C

 [self.tableView reloadData]; [self.tableView performBatchUpdates:^{} completion:^(BOOL finished) { /// table-view finished reload }]; 

迅速

 self.tableView?.reloadData() self.tableView?.performBatchUpdates({ () -> Void in }, completion: { (Bool finished) -> Void in /// table-view finished reload })