iOS:更新 tableViewCell 无限循环
Posted
技术标签:
【中文标题】iOS:更新 tableViewCell 无限循环【英文标题】:iOS: Updating tableViewCell infinite loop 【发布时间】:2013-03-20 15:10:39 【问题描述】:这就是问题所在:我在保存并获取 Core Data 的缩略图后更新了我的 tableview,然后我告诉单元格进行自我更新 - 这样当图像加载到 Core Data 中时它可以显示一个缩略图。我使用两个不同的线程,因为 Core Data 不是线程安全的,所有 GUI 元素当然都需要在主线程中发生。
但是这整个方法只是永远循环,而导致它的原因是当我重新加载线程时:
[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
为什么?我该如何解决这个问题?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Photo"];
Photo *photo = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = photo.title;
cell.detailTextLabel.text = photo.subtitle;
NSLog(@"Context %@", self.photographer.managedObjectContext);
[self.photographer.managedObjectContext performBlock:^
[Photo setThumbnailForPhoto:photo];
dispatch_async(dispatch_get_main_queue(), ^
cell.imageView.image = [UIImage imageWithData:photo.thumbnail];
[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
);
];
return cell;
【问题讨论】:
不要,我重复一遍,不要在 cellforrowatindexpath 中调用重新加载表(单元格)函数。因为它会一遍又一遍地获取图像。当您重新加载 tableview 或任何单元格时,它将再次调用 cellforrowatindexpath(在您的情况下一次又一次) cellForRowAtIndexPath 将在绘制单元格之前被调用。所以它已经更新了。为什么需要在 cellForRowAtIndexPath 中重新加载? Mert:我只是想在那里重新加载它,因为我可以直接在那里访问 tableViewCell。 【参考方案1】:无限循环是由于在加载fetch并调用某个单元格上的reload之后再次调用cellForRowAtIndexPath:
引起的。
重新加载将迫使 cellForRowAtIndexPath:
再次被调用...在你的情况下一次又一次地调用到无穷大。
解决方案很简单...不要在 cellForRowAtIndexPath 中重新加载您的单元格,而是在 fetchrequest 的回调方法中重新加载。然后在那里重新加载它而不是创建单元格。
根本不要在cellForRowAtIndexpath:
中加载图像。
每当您的表被实例化时,请创建一个循环您的数据源并获取每个项目的相应单元格的方法。然后为您认为需要的每个单元格加载图像。并在获取项目完成时重新加载单元格(例如回调方法)。
如果您确实希望像现在这样在创建单元格时加载图像(尽管我认为这不是正确的方法)。你可以用 if 语句包围整个 performBlock: 来检查图像是否已经设置。
【讨论】:
听起来不错!但是...我刚刚意识到我不知道回调方法是什么... 回调方法是在您的 fetchresult 完成时调用的方法。在这种情况下,获取图像。问题是,您需要在 cellForRowIndexpath 之外加载图像 他已经将异步调度到主队列作为回调。 没错,例如,您是否执行了回调方法。或者只是重新加载我认为不正确的代码。主要的是,当图像的加载在 cellForRowAtIndexpath 之外完成时,问题将得到解决:(这就是我一遍又一遍的意思) 但是如果他所做的只是用方法替换调度,他仍然会无限地调用这个(尽管我可能不知道 NSmanagedObjectContext performBlock 是如何工作的)【参考方案2】:正如其他人所说,加载图像时应该不需要调用reloadRowsAtIndexPaths
。为cell.imageView.image
分配一个新图像就足够了。
但是您的代码还有另一个问题。我假设self.photographer.managedObjectContext
是“私有并发类型”的托管对象上下文,因此performBlock
在后台线程上执行。 (否则就没有必要使用dispatch_async(dispatch_get_main_queue(), ...)
。)
所以performBlock:
在后台线程上异步执行代码。 (这很好,因为 UI 没有被阻塞。)但是当图像在一段时间后被提取时,单元格可能已重用用于不同的行。 (如果您滚动表格视图以使该行在获取图像时不可见,则会发生这种情况。)
因此,您必须检查单元格是否仍在表格视图中的相同位置:
[self.photographer.managedObjectContext performBlock:^
[Photo setThumbnailForPhoto:photo];
dispatch_async(dispatch_get_main_queue(), ^
if ([[tableView indexPathForCell:cell] isEqualTo:indexPath])
cell.imageView.image = [UIImage imageWithData:photo.thumbnail];
);
];
【讨论】:
【参考方案3】:我将替换以下代码:
cell.imageView.image = [UIImage imageWithData:photo.thumbnail];
[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
与:
UITableViewCell *blockCell = [tableView cellForRowAtIndexPath:indexPath];
blockCell.imageView.image = [UIImage imageWithData:photo.thumbnail];
[blockCell setNeedsLayout];
这解决了递归重新加载的问题,以及cell
在此期间被重用的可能性。我还会检查您是否已经加载了照片数据,如果是,则不更新。
由于您使用的是NSFetchedResultsController
,如果照片数据更新时委托回调已经通知您,您最好选择 Totomus Maximus 的回答。这种方式只会更新图像视图,不会更新照片更新方法可能更改的任何其他信息。
【讨论】:
以上是关于iOS:更新 tableViewCell 无限循环的主要内容,如果未能解决你的问题,请参考以下文章