4

我有UICollectionView一个自定义的实现UICollectionViewCell。数据数组是从 Internet 获取的,但图像是在用户访问内容时下载的。

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

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

在此先感谢,小齿轮

-(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;

}
4

1 回答 1

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:方法的单元格的更改。

一个警告是,如果您显示的内容列表可以更改(例如,如果用户可以随着时间的推移添加或删除项目),那么您的完成代码甚至可能不安全依赖索引路径保持不变。在这种情况下,您需要在图像到达时确定项目的索引路径。

于 2014-04-14T14:43:20.030 回答