2013-03-20 28 views
1

这就是问题所在:我在保存并将缩略图提取到Core Data后更新我的tableview,然后告诉单元更新自身 - 因此它可以在图像加载到时显示缩略图核心数据。我使用两个不同的线程作为核心数据不是线程安全的,当然所有的GUI元素都需要在主线程中发生。iOS:更新tableViewCell无限循环

但是这整个方法只是永远保持循环,并是什么原因造成的,当我重新加载线程:

[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; 
} 
+0

不要,我再说一遍,不要在cellforrowatindexpath内调用重载表(cell)函数..因为它会一遍又一遍地读取图像。当你重新加载tableview或任何单元格时,它会再次调用cellforrowatindexpath(并且在你的情况下再次调用) – 2013-03-20 15:13:43

+0

cellForRowAtIndexPath将在绘制单元格之前调用。所以它已经更新了。为什么你需要在cellForRowAtIndexPath中重新加载? – Mert 2013-03-20 15:15:28

+0

Mert:我只是应该重新加载它,因为我直接访问了tableViewCell。 – 2013-03-20 15:22:04

回答

3

无限循环再次调用cellForRowAtIndexPath:的获取被加载并在某小区重装被称为后引起的。

重新载入将迫使cellForRowAtIndexPath:再次被调用...并且在你的情况下一次又一次地无限。

解决方案很简单...不要在cellForRowAtIndexPath中重新加载单元格,而是在fetchrequest的回调方法中重新加载。然后重新加载它,而不是创建单元格。

相反,根本不要在cellForRowAtIndexpath:之内加载图像。 每当您的表被实例化时,都会创建一个循环遍历数据源并获取每个项目的相应单元格的方法。然后为您认为需要的每个单元格加载图像。并且在完成项目的提取时重新加载单元格(例如回调方法)。

如果您确实希望将图像加载到单元格创建中,就像您现在所做的那样(尽管我不认为这是正确的方法)。你可以围绕整个performBlock:用if语句检查图像是否已经被设置。

+0

听起来不错!但是......我只是意识到我不知道什么是回调方法...... – 2013-03-20 15:21:08

+0

回调方法是一种在您的fetchresult完成时调用的方法。在这种情况下获取图像。事情是,你需要在cellForRowIndexpath之外加载图像 – 2013-03-20 15:23:25

+0

他正在对主队列执行异步调度,因为回调已经完成。 – 2013-03-20 15:28:48

1

正如其他人已经说过的那样,图像加载完成后不需要调用reloadRowsAtIndexPaths。向cell.imageView.image分配新图像就足够了。

但是你的代码还有另一个问题。我假设self.photographer.managedObjectContext是“专用并发类型”的受管对象上下文,因此performBlock在后台线程上执行。 (否则不需要使用dispatch_async(dispatch_get_main_queue(), ...)。)

因此performBlock:在后台线程上异步执行代码。 (这是很好的,因为用户界面没有被阻塞。)但是当图像在一段时间后被提取时,单元格可能被重用用于不同的行。 (如果您滚动表格视图时会发生这种情况,以便在获取图像时该行不可见。)

因此,你必须要检查电池仍然在tableview中的相同位置:

[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]; 
     } 
    }); 
}]; 
1

我将以下代码:

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的答案。这种方式只是更新图像视图,并不会更新照片更新方法可能更改的任何其他信息。