简体   繁体   English

UICollectionView中的GCD和单元重用

[英]GCD and cell reuse in UICollectionView

I have a UICollectionView implementation with a custom UICollectionViewCell . 我有一个自定义UICollectionViewCellUICollectionView实现。 The array of data is fetched from the internet but the images are downloaded as the user is accessing the content. 从互联网获取数据阵列,但是当用户访问内容时下载图像。

While this is probably not the most efficient way and I do not plan on leave it this way, it did expose a certain issue which I am struggling to solve. 虽然这可能不是最有效的方式,我不打算以这种方式离开它,它确实暴露了我正在努力解决的某个问题。 It seems that the cells are displaying "cached" or "old" images and as I slowly scroll the collection view, the cell images keep on changing. 看起来细胞正在显示“缓存”或“旧”图像,当我慢慢滚动集合视图时,细胞图像不断变化。

This is probably an issue with the actual cell image not being available from the net at that moment in time and my question is how do I force an empty cell or some loading activitity monitor and not displaying an incorrect image? 这可能是一个问题,实际的细胞图像在那个时刻无法从网上获得,我的问题是如何强制空单元格或某些加载活动监视器而不显示不正确的图像?

Thanks in advance, pinion 提前谢谢,小齿轮

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

}

This is a common problem. 这是一个常见问题。 The big issue is that by the time the cell's image arrives, the cell may have been recycled and may be used for some other item. 最大的问题是,当细胞图像到达时,细胞可能已被回收并可用于其他一些物品。 If you try to set the image for the cell she the image is finally available, there's a good chance that you'll be setting the image for the wrong item. 如果您尝试为单元格设置图像,则图像最终可用,您很可能会为错误的项目设置图像。

So, don't try to update the cell directly. 因此,不要尝试直接更新单元格。 Don't even keep a reference to the cell in your image downloading completion block. 甚至不要在图像下载完成块中保留对单元格的引用。 Instead, have the completion block update the data model, whatever that looks like. 相反,让完成块更新数据模型,无论看起来如何。 You can also keep a weak reference to the collection view, and store the cell's index path. 您还可以对集合视图保持弱引用,并存储单元格的索引路径。 When the image arrives, the completion code can call -reloadItemsAtIndexPaths: , which will cause the collection view to reload the cell for the affected index path if the cell is still visible. 当图像到达时,完成代码可以调用-reloadItemsAtIndexPaths: ,如果单元格仍然可见,这将导致集合视图重新加载受影响的索引路径的单元格。 Your -collectionView:cellForItemAtIndexPath: method just does its usual thing, but this time the image will be available in the data model. 你的-collectionView:cellForItemAtIndexPath:方法只是做它常用的事情,但这次图像将在数据模型中可用。

So, your code will look something like: 所以,你的代码看起来像:

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

I haven't tested this exact code since I don't know what your data model looks like. 我没有测试过这个确切的代码,因为我不知道你的数据模型是什么样的。 Be sure to substitute your own data item class for the Event class that I assumed. 请务必将您自己的数据项类替换为我假设的Event类。 However, I use the same technique in my own code, and I think it's the right way to go. 但是,我在自己的代码中使用相同的技术,我认为这是正确的方法。 The highlights: 亮点:

  • Updating the image in your data model means that it will be available the next time the item needs to be displayed, even if the item isn't visible when the image is downloaded. 更新数据模型中的图像意味着下次需要显示项目时它将可用,即使在下载图像时该项目不可见。
  • Using a weak reference to the collection view avoids problems if images are still arriving when the user navigates away from the collection view. 如果用户导航离开集合视图时图像仍然到达,则使用对集合视图的弱引用可以避免出现问题。
  • Calling -reloadItemsAtIndexPaths: helps to limit changes to the cell to your ...cellForItemAtIndexPath: method. 调用-reloadItemsAtIndexPaths:有助于将单元格的更改限制为...cellForItemAtIndexPath:方法。

One caveat is that if the list of things you're displaying can change (eg if the user can add or delete items over time), then it may not even be safe for your completion code to rely on the index path staying the same. 需要注意的是,如果您正在显示的事物列表可能会发生变化(例如,如果用户可以随时间添加或删除项目),那么您的完成代码依赖于保持不变的索引路径甚至可能是不安全的。 In that case, you'll need to determine the index path for the item when the image arrives. 在这种情况下,您需要在图像到达时确定项目的索引路径。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM