如何检测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 } } }
这是我会做的。
-
在您的基类(可以是rootVC BaseVc等),
A.写一个协议发送“DidFinishReloading”callback。
@protocol ReloadComplition <NSObject> @required - (void)didEndReloading:(UITableView *)tableView; @end
B.编写一个通用的方法来重新加载表视图。
-(void)reloadTableView:(UITableView *)tableView withOwner:(UIViewController *)aViewController;
-
在基类方法实现中,延迟调用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]; } }]; }
-
在所有需要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 })