Jul*_*ert 7 objective-c uitableview reloaddata ios
我实现了一个UITableView带有更多功能的负载.tableView从有时很慢的服务器加载大图像.我正在为每个图像启动一个URLConnection并重新加载对应于URLConnection的indexPath(与连接对象一起保存).连接本身调用-reloadDatatableView.
现在,当单击加载更多按钮时,我滚动到位置底部的新数据集的第一行.这很好用,也是我的异步加载系统.
我面临以下问题:当连接"太快"时,tableView正在重新加载给定indexPath的数据,而tableView仍然滚动到新数据集的第一个单元格,tableView向后滚动一半的高度细胞.
这应该是它应该是什么样子以及它实际上做了什么:

^^^^^^^^^^^^ should ^^^^^^^^^^^^ ^^^^^^^^^^^^^ does ^^^^^^^^^^^^^
以下是一些代码:
[[self tableView] beginUpdates];
for (NSMutableDictionary *post in object) {
[_dataSource addObject:post];
[[self tableView] insertRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:[_dataSource indexOfObject:post] inSection:0]] withRowAnimation:UITableViewRowAnimationBottom];
}
[[self tableView] endUpdates];
[[self tableView] scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:[_dataSource indexOfObject:[object firstObject]] inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
Run Code Online (Sandbox Code Playgroud)
-tableView:cellForRowAtIndexPath:如果数据源数组中的对象是字符串,则启动JWURLConnection,并将其替换UIImage为完成块中的实例.然后它重新加载给定的单元格:
id image = [post objectForKey:@"thumbnail_image"];
if ([image isKindOfClass:[NSString class]]) {
JWURLConnection *connection = [JWURLConnection connectionWithGETRequestToURL:[NSURL URLWithString:image] delegate:nil startImmediately:NO];
[connection setFinished:^(NSData *data, NSStringEncoding encoding) {
[post setObject:[UIImage imageWithData:data] forKey:@"thumbnail_image"];
[tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}];
[cell startLoading];
[connection start];
}
else if ([image isKindOfClass:[UIImage class]]) {
[cell stopLoading];
[cell setImage:image];
}
else {
[cell setImage:nil];
}
Run Code Online (Sandbox Code Playgroud)
-reloadRowsAtIndexPaths:withRowAnimation:在tableView滚动完成之前,我可以阻止tableView执行调用吗?或者你能想象一个防止这种行为的好方法吗?
基于Malte和savner的想法(请同时回答他的回答)我可以实施一个解决方案.他的回答没有成功,但这是正确的方向.
我必须实施-scrollViewDidEndScrollingAnimation:.我创建了一个名为bool的属性,_autoScrolling并NSMutableArray为滚动时重新加载的索引路径创建了一个属性.在URLConnections完成块我做了这个:
if (_autoScrolling) {
if (!_indexPathsToReload) {
_indexPathsToReload = [NSMutableArray array];
}
[_indexPathsToReload addObject:indexPath];
}
else {
[tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
Run Code Online (Sandbox Code Playgroud)
然后这个:
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {
[self performSelector:@selector(performRelodingAfterAutoScroll) withObject:nil afterDelay:0.0];
}
- (void)performRelodingAfterAutoScroll {
_autoScrolling = NO;
if (_indexPathsToReload) {
[[self tableView] reloadRowsAtIndexPaths:_indexPathsToReload withRowAnimation:UITableViewRowAnimationFade];
}
_indexPathsToReload = nil;
}
Run Code Online (Sandbox Code Playgroud)
我花了很长时间才找到诀窍,但-performSelector:withObject:afterDelay:我仍然不知道为什么需要它.
我认为该方法可能过早被调用.所以我实施了一秒钟的延迟,并尝试了我可以把它取下来.它仍然可以使用,0.0但如果我直接调用该方法或使用它-performSelector:withObject:.
我真的希望有人可以解释一下.
编辑
几年后重新审视后,我可以解释这里发生了什么:
调用-[NSObject (NSDelayedPerforming) performSelector:withObject:afterDelay:]保证在下一次runloop迭代中执行调用.
所以更好或恕我直言更美丽的解决方案是:
[[NSOperationQueue currentQueue] addOperationWithBlock:^{
[self performRelodingAfterAutoScroll];
}];
Run Code Online (Sandbox Code Playgroud)
我在这个答案中写了一个更详细的解释.
抱歉,我没有足够的声誉来添加评论,因此在单独的答案中回答您的上一个问题.
-performSelector:withObject:afterDelay: 延迟0.0秒不会立即执行给定的选择器,而是在当前的Runloop Cycle结束后和给定的延迟之后执行它.
在-performSelector:withObject:当前的Runloop循环中添加和执行的位置.这与直接调用该方法相同.
因此,使用-performSelector:withObject:afterDelay:UI将在当前的Runloop Cycle中更新,即在这种情况下,滚动动画可以在执行选择器之前完成(并再次重新加载UI).
| 归档时间: |
|
| 查看次数: |
8801 次 |
| 最近记录: |