UICollectionView 在滚动时重新加载错误的图像
Posted
技术标签:
【中文标题】UICollectionView 在滚动时重新加载错误的图像【英文标题】:UICollectionView reloads wrong images on scroll 【发布时间】: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]];
有什么想法吗?谢谢。
【问题讨论】:
【参考方案1】:我不确定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
【讨论】:
那应该是这样吗? 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。
希望这会对你有所帮助。
【讨论】:
以上是关于UICollectionView 在滚动时重新加载错误的图像的主要内容,如果未能解决你的问题,请参考以下文章
UICollectionView 单元格在可见时不断重新加载
在 UICollectionView 中重新加载数据后保持 collectionview 速度
iOS 9 UICollectionView 水平重新加载数据不起作用
UICollectionView insertItemsAtIndexPaths:似乎重新加载collectionview或自动滚动到顶部