UICollectionView在滚动中重新加载错误的图像

在下面的代码中,我从文本文件中下载图像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]]; } 

有任何想法吗? 谢谢。

我注意到一堆没有评论的downvotes,再次阅读我的答案后,我想原来接受的答案(在线以下)可能会更好。

这类问题有几件事情正在进行。 UITableView只创build足够的UITableViewCells来显示视图中的每个单元格以及滚动到视图中的内容。 其他单元格将在滚动查看后重新使用。

这意味着当你快速滚动时,单元格被设置多次,以获取图像asynchronous,然后显示它,因为它很慢地获取图像。 加载图像后,它会显示在最初调用它的单元格上,但该单元格可能已被重新使用。

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

所以正确的做法是:

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

所以有一个对象在asynchronous调用中传递,因此可以存储提取的图像。 当前单元格中还有另一个对象可以与之比较。 在获取图像之前,单元格通常已被重用,因此在下载图像时,这些对象是不同的。


滚动和旋转CollectionViews总是有点问题。 我总是有同样的问题,出现在错误的单元格中的图像。 收集视图有一些优化会影响图像而不是标签。 对我来说这是一个错误,但它仍然没有解决三个iOS版本之后。

您需要执行以下操作:

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

这应该总是返回YES基本上总是刷新它。 如果这解决了你的问题,那么你可以考虑不执行整个重绘,因为它不利于性能。 我个人花了很多时间来计算,如果一个屏幕已经通过或线路已经通过等,但它变成了结构,所以我最终只是返回YES。 因人而异。

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


在你的callback块(当你回到主队列),你指的是recipeImageView 。 但是,如果用户在下载期间滚动,则cell对象已被重新使用。

一旦你在callback中,你必须假定所有对视图的引用( cellrecipeImageViewblurView等等都指向错误的东西,你需要使用你的数据模型来find正确的视图。

一个常见的方法是使用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: ,它应该代表你处理所有的asynchronous加载。 如果没有,我想推荐SDWebImage库,它的确如此: https : //github.com/rs/SDWebImage

我通过在应用程序启动时下载NSArray解决了这个问题,所以它被存储在本地。