UICollectionView滚动时重新加载错误的图片

10
在下面的代码中,我从一个文本文件中下载图像URL,并将其存储在NSMutableArray中,然后在CellforItemAtIndexPath中从这些URLS下载图像。但是,当我上下滚动时,错误的图像会在错误的位置出现一小段时间。它在一秒钟后就会纠正,但我想要解决这个滚动问题。以下是代码:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
    static NSString *identifier = @"Cell2";

    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
    AsyncImageView *recipeImageView = (AsyncImageView *)[cell viewWithTag:100];
    UILabel *titleView = (UILabel *)[cell viewWithTag:101];
    titleView.numberOfLines = 3;
    UIImageView *overlay = (UIImageView *)[cell viewWithTag:111];
    FXBlurView *blurView = (FXBlurView *)[cell viewWithTag:110];
    blurView.tintColor = [UIColor colorWithRed:51.0 green:51.0 blue:51.0 alpha:1];


    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
        hi = [self videoTitle];
        images = [self firstPhoto];
        // switch to a background thread and perform your expensive operation


        // switch back to the main thread to update your UI


        dispatch_async(dispatch_get_main_queue(), ^{
            NSString *imgSrc;

            NSString *url = images[indexPath.row];
            imgSrc = url;
            if (imgSrc != nil && [imgSrc length] != 0 ) {
                [recipeImageView setImageWithURL:[NSURL URLWithString:images[indexPath.row]] placeholderImage:[UIImage imageNamed:@"red.png"]];

            }
            else {
                NSLog(@"noimage");
                recipeImageView.image = [UIImage imageNamed:@"red.png"];
            }
            titleView.text = hi[indexPath.row];


        });
    });

    return cell;


    // recipeImageView.image = [UIImage imageNamed:[videoList objectAtIndex:indexPath.row]];

}

有什么想法吗?谢谢。

3个回答

6

我不确定AsyncImageView是什么,所以让我首先回答你的问题,就好像它是一个UIImageView


在你的回调块中(当你返回到主队列时), 你引用了 recipeImageView。然而,如果用户在下载期间滚动了屏幕,那么cell对象已经被重用。

一旦进入回调,你必须假定所有对视图的引用(cell, recipeImageView, blurView等)都指向错误的对象。你需要使用你的数据模型查找正确的视图。

一种常见的方法是使用NSIndexPath来查找单元格:

UICollectionViewCell *correctCell = [collectionView cellForItemAtIndexPath:indexPath]:
UIImageView *correctRecipeImageView = (UIImageView *)[correctCell viewWithTag:100];
/* Update your image now… */

请注意,此方法仅在indexPath始终指向相同的内容时才有效。如果用户可以插入/删除行,则还需要找出正确的indexPath,因为它也可能已更改。
NSIndexPath *correctIndexPath  = /* Use your data model to find the correct index path */
UICollectionViewCell *correctCell = [collectionView cellForItemAtIndexPath:correctIndexPath]:
UIImageView *correctRecipeImageView = (UIImageView *)[correctCell viewWithTag:100];
/* Update your image now… */

如果 AsyncImageView 是另一个类,它可以为您处理所有这些内容,那么您只需在主线程上调用 setImageWithURL: placeholderImage:,它应该代表您处理所有异步加载。 如果它没有完成,我建议使用 SDWebImage 库: https://github.com/rs/SDWebImage


所以应该像这样吗?http://pastie.org/8751762。但是它不起作用,仍然存在同样的问题(我只是将其制作为UIImageView)。 - user1941966
SDWebImage在处理滚动问题方面表现更好。谢谢@AaronBrager - Andoxko
永远不要使用viewWithTag,它太脆弱了。至少在您的单元格可以使用@IBOutlet时不要使用它。 - Lucas van Dongen
@DepartamentoB 您是正确的,子类化和属性是更好的解决方案,但在这种情况下,OP没有子类化UICollectionViewCell,我发布的解决方案是使其工作所需的最小更改。我不认为我同意您应该“永远”不使用此技术。 - Aaron Brager

2
我注意到有很多没有评论的踩和再次阅读我的回答后,我想原来被接受的答案(下面的那个)可能可以更好。这种问题有几个要点。UITableView只创建足够的UITableViewCells来显示每个可见单元格以及将要滚动到视图中的单元格。其他的单元格在滚出视图后会被重新使用。
这意味着当你快速滚动时,单元格会被设置多次以异步获取并显示图片,因为获取图片非常慢。图片加载完成后,它会显示在最初调用获取它的单元格上,但该单元格可能已经被重复使用了。
现在,我在用作UITableViewCells数据的任何数据类上都有一个bufferedImage: UIImage?属性。这将最初始终为nil,因此我异步获取图像,并且当dispatch_async返回时,它将存储在那里,因此下次需要显示此单元格时,它已经准备好了,如果仍然是相同的单元格,我也会在其中显示它。
因此,正确的方法是:
  • 开始使用您的数据对象配置单元格
  • 将数据对象存储在单元格中,以便稍后引用
  • 检查数据对象是否已经在bufferedImage中设置了图像,如果是,则显示它,就这样
  • 如果还没有图像,请将当前图像重置为无或占位符,并开始在后台下载图像
  • 当异步调用返回时,将图像存储在bufferedImage属性中
  • 检查存储在当前单元格中的对象是否仍然是您获取图像的相同对象(这非常重要),如果不是,则不执行任何操作
  • 如果您仍在同一单元格中,请显示图像

因此,在异步调用中传递了一个对象,以便可以存储获取的图像。还有另一个对象在当前单元格中,您可以进行比较。通常在您获取图像之前,单元格已被重用,因此这些对象在您下载图像时已经不同。


滚动和旋转CollectionView一直是一个有些棘手的问题。我总是遇到同样的问题,就是图片出现在错误的单元格中。对于影响图像但不影响标签的collectionview,存在某种优化。对我来说这是一个bug,但即使在三个iOS版本之后仍未解决。
你需要实现以下内容:

https://developer.apple.com/library/ios/documentation/uikit/reference/UICollectionViewLayout_class/Reference/Reference.html#//apple_ref/occ/instm/UICollectionViewLayout/shouldInvalidateLayoutForBoundsChange:

这个基本上应该始终返回YES,以便始终刷新它。如果这解决了您的问题,那么您可以考虑不每次都执行整个重绘,因为这会影响性能。我个人花了很多时间计算屏幕是否已经通过或者行是否已经通过等等,但结果变得不稳定,所以最终还是选择了返回YES。你的情况可能会有所不同。

0

我看到你已经回答了这个问题,但还是想分享我的经验。

我也遇到了同样的问题。

在我的情况下,这个问题的原因是以下函数:collectionView.dequeueReusableCell(...)

每种情况下单元格的类型都是相同的,但数据是不同的:

  • 在某些情况下,特定单元格的数据包含图像。
  • 在某些情况下,特定单元格的数据不包含图像,而是nil。

当图像为nil时,我没有将图像设置到imageView中。在这种情况下,集合视图重用其他单元格的图像(当前单元格的错误图像)。

解决方案非常简单:如果imageView不包含图像,则只需设置为nil即可。

希望这能帮助到你。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接