【问题标题】:UICollectionView reloads wrong images on scrollUICollectionView 在滚动时重新加载错误的图像
【发布时间】:2014-02-19 15:25:57
【问题描述】:

在下面的代码中,我从文本文件下载图像 URL,将其存储在 NSMutableArray 中,然后从 CellforItemAtIndexPath 中的这些 URL 下载图像。但是,当我上下滚动片刻时,错误的图像出现在错误的位置。一秒钟后它会更正,但我想摆脱那个滚动问题。代码如下:

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

}

有什么想法吗?谢谢。

【问题讨论】:

    标签: ios uicollectionview afnetworking


    【解决方案1】:

    我不确定AsyncImageView是什么,所以首先让我像UIImageView一样回答你的问题:


    在您的回调块中(当您返回主队列时),您指的是recipeImageView。但是,如果用户在下载过程中进行了滚动,则 cell 对象已被重用。

    一旦您进入回调,您必须假设您对视图的所有引用(cellrecipeImageViewblurView 等都指向错误的东西。您需要 使用您的数据模型找到正确的视图。

    一种常见的方法是使用 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

    【讨论】:

    • 那应该是这样吗? pastie.org/8751762。这个不行,还是一样的问题(我只是做了个UIImageView)。
    • SDWebImage 可以更好地解决滚动问题。谢谢@AaronBrager
    • 永远不要使用viewWithTag,它太脆弱了。至少当你的单元格可以有一个 @IBOutlet 时不会。
    • @DepartamentoB 您是正确的,子类化和属性是更好的解决方案,但在这种情况下,OP 不是子类化 UICollectionViewCell,我发布的解决方案是使其工作所需的最小更改。我不认为我同意你应该“永远”使用这种技术
    【解决方案2】:

    我注意到一堆没有 cmets 的反对票,在再次阅读我的答案后,我猜最初接受的答案(在行下方)可能会更好。

    这类问题会发生一些事情。 UITableView 只创建足够的 UITableViewCells 来显示视图中的每个单元格以及将滚动到视图中的内容。其他单元格在滚动出视图后将被重新使用。

    这意味着当您快速滚动时,单元格会设置多次以异步获取图像然后显示它,因为它获取图像的速度非常慢。图像加载后,它将显示在最初调用来获取它的单元格上,但该单元格可能已经被重复使用。

    现在我有一个缓冲图像:UIImage?我用作 UITableViewCells 数据的任何数据类的属性。这最初总是为零,所以我异步获取图像,当 dispatch_async 返回时它将存储在那里,所以下次我需要显示这个单元格时它已经准备好了,如果它仍然是同一个单元格,我也会在单元格中显示它.

    所以正确的做法是:

    • 开始使用您的数据对象配置单元格
    • 将数据对象存储在单元格中,以便以后引用
    • 检查数据对象是否已经在 bufferedImage 中设置了图像,如果是,则显示它,就是这样
    • 如果还没有图像,请将当前图像重置为空或占位符,然后开始在后台下载图像
    • 当异步调用返回时,将图像存储在 bufferedImage 属性中
    • 检查存储在当前单元格中的对象是否仍然是您为其提取图像的对象(这非常重要),如果不是,则不执行任何操作
    • 如果您仍在同一个单元格中,请显示图片

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


    滚动和旋转 CollectionViews 总是有点问题。我总是有同样的问题,图像出现在错误的单元格中。对影响图像但不影响标签的 collectionview 进行了某种优化。对我来说这是一个错误,但在三个 iOS 版本之后仍未解决。

    您需要实现以下内容:

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

    这应该始终返回 YES 以始终刷新它。如果这解决了您的问题,那么您可以考虑不每次都执行整个重绘,因为这对性能不利。我个人花了很多时间来计算屏幕是否通过或线路是否通过等等,但它变得结结巴巴,所以我最终只返回“是”。 YMMV。

    【讨论】:

      【解决方案3】:

      我看到您已经回答了这个问题,但仍然想分享我的经验。

      我遇到了同样的问题。

      就我而言,这个问题的原因是以下函数:collectionView.dequeueReusableCell(...)。

      每个案例的单元格类型都相同,但数据不同:

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

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

      解决方法很简单:如果imageView不包含图片,就设置nil。

      希望这会对你有所帮助。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-10-23
        • 1970-01-01
        • 2017-07-10
        • 2016-02-29
        • 2016-05-03
        • 1970-01-01
        相关资源
        最近更新 更多