我试图在 UITableView 执行[self.tableView reloadData]
后滚动到底部。
我原来有
[self.tableView reloadData]
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 是异步的,所以滚动不会发生,因为 self.tableView
、[self.tableView numberOfSections]
和 [self.tableView numberOfRowsinSection
都是 0。
谢谢!
奇怪的是,我使用的是:
[self.tableView reloadData];
NSLog(@"Number of Sections %d", [self.tableView numberOfSections]);
NSLog(@"Number of Rows %d", [self.tableView numberOfRowsInSection:([self.tableView numberOfSections]-1)]-1);
在控制台中,它返回 Sections = 1, Row =-1;
当我在 cellForRowAtIndexPath
中执行完全相同的 NSLogs 时,我得到 Sections = 1 和 Row = 8;(8 是对的)
重新加载发生在下一次布局过程中,通常发生在将控制权返回运行循环时(例如,在按钮操作或其他操作返回后)。
因此,在表格视图重新加载后运行的一种方法就是强制表格视图立即执行布局:
[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
)。
斯威夫特:
extension UITableView {
func reloadData(completion: ()->()) {
UIView.animateWithDuration(0, animations: { self.reloadData() })
{ _ in completion() }
}
}
...somewhere later...
tableView.reloadData {
println("done")
}
目标-C:
[UIView animateWithDuration:0 animations:^{
[myTableView reloadData];
} completion:^(BOOL finished) {
//Do something after that...
}];
看来大家还在阅读这个问题和答案。有鉴于此,我正在修改我的答案,去掉同步这个词,因为它与这个问题确实无关。
当 [tableView reloadData]` 返回时,tableView 后面的内部数据结构已经更新。因此,当该方法完成时,你可以安全地滚动到底部。我在自己的应用程序中验证了这一点。@rob-mayoff 的答案虽然在术语上也很混乱,但在他的最后一次更新中也承认了这一点。
如果您的 tableView
没有滚动到底部,您可能在其他未发布的代码中遇到了问题。也许您在滚动完成后更改了数据,但却没有重新加载和/或滚动到底部?
添加以下日志记录,以验证reloadData
后的表格数据是否正确。我在示例应用程序中使用了以下代码,效果非常好。
// change the data source
NSLog(@"Before reload / sections = %d, last row = %d",
[self.tableView numberOfSections],
[self.tableView numberOfRowsInSection:[self.tableView numberOfSections]-1]);
[self.tableView reloadData];
NSLog(@"After reload / sections = %d, last row = %d",
[self.tableView numberOfSections],
[self.tableView numberOfRowsInSection:[self.tableView numberOfSections]-1]);
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:[self.tableView numberOfRowsInSection:[self.tableView numberOfSections]-1]-1
inSection:[self.tableView numberOfSections] - 1]
atScrollPosition:UITableViewScrollPositionBottom
animated:YES];