UICollectionView中的GCD和单元重用

时间:2013-04-29 08:04:27

标签: ios uicollectionview uicollectionviewcell

我有一个UICollectionView实施,其中包含自定义UICollectionViewCell。 从互联网获取数据阵列,但是当用户访问内容时下载图像。

虽然这可能不是最有效的方式,但我不打算以这种方式离开它,它确实暴露了我正在努力解决的某个问题。似乎单元格正在显示“缓存”或“旧”图像,当我慢慢滚动集合视图时,单元格图像会不断变化。

这可能是一个问题,实际的细胞图像在那个时刻无法从网上获得,我的问题是如何强制空单元格或某些加载活动监视器而不显示不正确的图像?

提前致谢,小齿轮

-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString * CellIdentifier = @"Event";

EventCollectionViewCell *cell  = [collectionView dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath];


NSString *title = [[events objectAtIndex:indexPath.item] objectForKey:@"title"];
NSString *imageUrl = [[[events objectAtIndex:indexPath.item] objectForKey:@"photo"] objectForKey:@"url"];


dispatch_queue_t imageFetchQ = dispatch_queue_create("image fetched", NULL);
dispatch_async(imageFetchQ, ^{

    NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageUrl]];

    if (imageData)
    {

        dispatch_async(dispatch_get_main_queue(), ^{

            cell.eventImage.image = [UIImage imageWithData:imageData];
            cell.eventTitle.text = title;

        });
    }
});


return cell;

}

1 个答案:

答案 0 :(得分:5)

这是一个常见问题。最大的问题是,当细胞图像到达时,细胞可能已被回收并可用于其他一些物品。如果您尝试为单元格设置图像,则图像最终可用,您很可能会为错误的项目设置图像。

因此,请勿尝试直接更新单元格。甚至不要在图像下载完成块中保留对单元格的引用。相反,让完成块更新数据模型,无论看起来如何。您还可以对集合视图保持弱引用,并存储单元格的索引路径。当图像到达时,完成代码可以调用-reloadItemsAtIndexPaths:,如果单元格仍然可见,这将导致集合视图重新加载受影响的索引路径的单元格。您的-collectionView:cellForItemAtIndexPath:方法只是常用,但这次图像将在数据模型中可用。

因此,您的代码将类似于:

-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString * CellIdentifier = @"Event";
    EventCollectionViewCell *cell  = [collectionView dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath];

    Event *event = [events objectAtIndex:indexPath.item];  // replace "Event" with whatever class you use for your items
    cell.eventTitle.text = [event objectForKey:@"title"];
    cell.eventImage.image = [event objectForKey:@"image"];
    if (cell.eventImage.image == nil) {
        NSString *imageUrl = [[[events objectAtIndex:indexPath.item] objectForKey:@"photo"] objectForKey:@"url"];

        dispatch_queue_t imageFetchQ = dispatch_queue_create("image fetched", NULL);
        dispatch_async(imageFetchQ, ^{
            __weak UICollectionView *weakCollection;
            NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageUrl]];
            UIImage *image = [UIImage imageWithData:imageData];
            if (image)
            {
                dispatch_async(dispatch_get_main_queue(), ^{
                    [event setObject:image forKey:@"image"]; // updating the model here
                    [weakCollection reloadItemsAtIndexPaths:@[indexPath]];
                });
            }
        });
    }
    return cell;
}

我没有测试过这个确切的代码,因为我不知道你的数据模型是什么样的。请务必将您自己的数据项替换为我假设的Event类。但是,我在自己的代码中使用相同的技术,我认为这是正确的方法。亮点:

  • 更新数据模型中的图像意味着下次需要显示项目时它将可用,即使下载图像时该项目不可见。
  • 如果用户导航离开集合视图时图像仍然到达,则使用对集合视图的弱引用可以避免出现问题。
  • 致电-reloadItemsAtIndexPaths:有助于将对单元格的更改限制为...cellForItemAtIndexPath:方法。

有一点需要注意的是,如果您显示的内容列表可能会发生变化(例如,如果用户可以随时间添加或删除项目),那么您的完成代码依赖于索引路径可能不安全相同。在这种情况下,您需要在图像到达时确定项目的索引路径。