UITableView何时完成ReloadData的操作?
UITableView何时完成ReloadData的操作?
在 UITableView 执行完 [self.tableView reloadData] 后,我希望滚动到底部。\n最初我写了以下代码:\n[self.tableView reloadData];\nNSIndexPath* indexPath = [NSIndexPath indexPathForRow:([self.tableView numberOfRowsInSection:([self.tableView numberOfSections]-1)]-1) inSection:([self.tableView numberOfSections]-1)];\n[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];\n但是后来我了解到 reloadData 是异步的,所以滚动操作无法正常进行,因为此时 self.tableView、[self.tableView numberOfSections] 和 [self.tableView numberOfRowsinSection] 的值都为0。\n奇怪的是我使用了以下代码:\n[self.tableView reloadData];\nNSLog(@\"Number of Sections %d\", [self.tableView numberOfSections]);\nNSLog(@\"Number of Rows %d\", [self.tableView numberOfRowsInSection:([self.tableView numberOfSections]-1)]-1);\n在控制台中的输出是 Sections = 1,Row = -1;\n但是当我在 cellForRowAtIndexPath 中执行相同的 NSLog 时,输出是 Sections = 1,Row = 8(8 是正确的值)。
UITableView的reloadData()方法是用于重新加载表格数据的,当表格数据发生改变时需要调用该方法以更新表格的显示。但是,由于reloadData()方法是同步执行的,即会阻塞主线程,所以当调用reloadData()方法后,需要等待表格数据完全加载完成后才能进行一些后续操作。
为了判断UITableView何时完成reloadData()的操作,可以使用CATransaction的setCompletionBlock()方法,在其中添加完成后的处理代码。具体步骤如下:
1. 调用CATransaction的begin()方法,开始一个事务。
2. 使用setCompletionBlock()方法设置一个闭包,在闭包中添加reloadData()完成后的处理代码。
3. 执行reloadData()方法,重新加载表格数据。
4. 调用CATransaction的commit()方法,提交事务。
以上方法同样适用于UICollectionView的reloadData()和UIPickerView的reloadAllComponents()方法。
如果在使用beginUpdates()和endUpdates()方法手动插入、删除或移动行时,也可以使用上述方法判断操作是否完成。
总结一下,使用CATransaction的setCompletionBlock()方法可以判断UITableView何时完成reloadData()操作,从而进行后续处理。
以下是一段示例代码,演示了如何使用CATransaction判断UITableView何时完成reloadData()操作:
CATransaction.begin()
CATransaction.setCompletionBlock({
print("reload completed")
// Your completion code here
})
print("reloading")
tableView.reloadData()
CATransaction.commit()
以上代码可以在Xcode 8.2.1、iOS 10和Swift 3环境下使用,有效解决了判断UITableView何时完成reloadData()的问题。
需要注意的是,有些用户可能会遇到一些特殊情况,比如在setCompletionBlock()闭包中调用tableView的某些方法返回的结果不符合预期。这可能是因为闭包执行的时机与reloadData()的执行顺序有关,或者涉及到其他因素。在这种情况下,可以尝试其他解决方法,比如Tyler提供的解决方案。
总之,使用CATransaction的setCompletionBlock()方法可以有效判断UITableView何时完成reloadData()的操作,并在完成后进行后续处理。这是一种常用且现代的解决方法。
在使用UITableView的reloadData方法时,我们可能需要知道何时reloadData已经完成。但是,由于reloadData是在主线程中执行的,所以无法直接使用completion block来判断reloadData是否已经完成。以上提供了两种解决方案,一种是使用UIView的animateWithDuration方法来包装reloadData,并在completion block中执行相关操作;另一种是使用dispatch_after方法延迟执行相关操作。通过这两种方法,我们可以在reloadData完成后执行一些操作,如打印"done"等。
在Swift中,我们可以通过扩展UITableView来实现reloadData的带有completion的版本。在这个扩展中,我们使用UIView的animateWithDuration方法来包装reloadData,并在completion block中执行传入的completion闭包。这样,当reloadData完成时,我们可以在completion中执行相关操作。
在Objective-C中,我们可以使用UIView的animateWithDuration方法来包装reloadData,并在completion block中执行相关操作。在这个例子中,我们使用了block语法来定义completion block,并在其中执行我们想要的操作。
以上两种方法都可以实现在reloadData完成后执行一些操作的目的。需要注意的是,这种方法并不被建议使用,因为它可能导致意想不到的行为。如果你确实需要在reloadData完成后执行一些操作,可以使用以上提供的解决方案。
另外,还有其他一些解决方案。比如,可以使用dispatch_after方法来延迟执行相关操作。这种方法可以在reloadData之后的某个时间点执行操作,以确保reloadData已经完成。
,当我们需要在UITableView的reloadData完成后执行一些操作时,我们可以使用上述提供的解决方案。无论是使用UIView的animateWithDuration方法还是使用dispatch_after方法,都可以实现在reloadData完成后执行一些操作的目的。但需要注意的是,这种做法可能导致意想不到的行为,所以在使用时请谨慎。
如何判断UITableView何时完成ReloadData?
UITableView的reload操作发生在下一次布局时,通常是在你将控制权返回给运行循环之后发生(比如你的按钮动作返回后)。
所以一种在表格视图重新加载后运行某些操作的方法是强制表格视图立即执行布局:
[self.tableView reloadData]; [self.tableView layoutIfNeeded]; NSIndexPath* indexPath = [NSIndexPath indexPathForRow: ([self.tableView numberOfRowsInSection:([self.tableView numberOfSections]-1)]-1) inSection: ([self.tableView numberOfSections]-1)]; [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
另一种方法是使用dispatch_async在稍后运行你的布局代码:
[self.tableView reloadData]; dispatch_async(dispatch_get_main_queue(), ^{ NSIndexPath* indexPath = [NSIndexPath indexPathForRow: ([self.tableView numberOfRowsInSection:([self.tableView numberOfSections]-1)]-1) inSection:([self.tableView numberOfSections]-1)]; [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES]; });
进一步调查后,我发现在从reloadData返回之前,表格视图会向其数据源发送tableView:numberOfSections:和tableView:numberOfRowsInSection:消息。如果代理实现了tableView:heightForRowAtIndexPath:,表格视图还会在从reloadData返回之前发送该消息(对于每一行)。
然而,表格视图直到布局阶段才会发送tableView:cellForRowAtIndexPath:或tableView:headerViewForSection:消息,默认情况下,布局阶段发生在将控制权返回给运行循环时。
我还发现在一个小的测试程序中,你在问题中的代码能够正常滚动到表格视图的底部,而我并没有做任何特殊的处理(比如发送layoutIfNeeded或使用dispatch_async)。
我喜欢第二种方法。对于这种情况,我非常喜欢延迟执行。而且GCD(dispatch_async)使得代码整洁而简单。
根据你的表格数据源的大小,你可以在同一个运行循环中以动画的方式滚动到表格视图的底部。如果你尝试使用一个庞大的表格来测试你的代码,你会发现使用GCD延迟滚动到下一个运行循环将会成功,而立即滚动则会失败。但无论如何,感谢你提供的这个技巧!
方法2对我来说无法正常工作,原因未知,但我选择了第一种方法。
谢谢你的回答。使用layoutIfNeeded方法解决了我在某些文本视图中遇到的一些疯狂的时序问题。
这两种方法对我都不起作用。我使用的是iOS 9,希望这个信息能有所帮助。
我在视图加载完成时遇到了一个动态行高的问题,当我在viewDidLoad中进行滚动时,它无法滚动到表格视图的最底部。使用第二种解决方法对我起了作用。
使用dispatch_async(dispatch_get_main_queue())方法不能保证一直起效。我在使用该方法时遇到了非确定性行为,有时系统在完成layoutSubviews和cell渲染之前,有时在之后。我将在下面发布一个对我有效的答案。
同意dispatch_async(dispatch_get_main_queue())并不总是起作用。这里出现了随机结果。
还有其他人认为依赖线程模型不是一个好主意。我可以通过在容器视图控制器中使用子视图控制器(如容器视图控制器中的子视图控制器)来始终使(dispatch_async)方法无效。对我来说,使用layoutIfNeeded强制进行布局非常好用。
如果我只在主线程上调用reloadData,那么由于主线程是同步的,这不意味着之后的所有操作都会在reloadData完成之后发生吗?或者即使在主线程上,reloadData本身总是在后台线程上运行,所以需要其他机制来解决这个问题,因此才有了这个问题...
主线程运行着一个NSRunLoop。RunLoop有不同的阶段,你可以在特定阶段安排一个回调(使用CFRunLoopObserver)。UIKit将布局安排在稍后的阶段,也就是在你的事件处理程序返回后。
谢谢。1)你的最终答案是什么?如果你把它放在主线程上,它不会等到reloadData完成吗?是或否?我认为答案是是,但是布局(另一件事情)被安排在稍后执行。2)你所说的在事件处理程序返回后是指什么时刻/事件?你是指reloadData事件吗?你是否意味着视图/UI的布局被安排在稍后执行...但我们现在需要它?所以这个问题出现了...3)我之前有关runloop的知识,但还不够全面。有没有特定的关键字,我可以搜索并学习你刚才说的内容?
我对你的回答进行了编辑,但说实话,在进行编辑后,我自己有点困惑,不确定它是否正确。你能纠正一下编辑,而不只是回滚它吗?
我删除了你的大部分编辑,因为它提出了我没有亲自验证过的主张。
朋友们,在我的情况下,我需要在重新加载后获取单元格,我使用了以下代码:
DispatchQueue.main.async {
let indexPath = IndexPath(row: 5, section: 0)
let defaultCell = self.tableView.dequeueReusableCell(withIdentifier: CreditCardTableViewCell.cellIdentifier, for: indexPath)
self.tableView.scrollRectToVisible(defaultCell.frame, animated: true)
}
强制重新加载没有起作用,但将其放在主队列中起作用了。