我有一个UICollectionView
,但同样的方法应该适用于UITableViews
。 我的每一个细胞的包含一个图像I从磁盘,它是一个缓慢的操作负担。 为了缓解这个问题,我使用异步调度队列。 这工作得很好,但在这些操作中快速滚动的效果叠加起来,使得细胞将其形象从一个到另一个顺序改变,直到最后的最后一次通话停止。
在过去,我已经做了检查,看是否牢房仍清晰可见,如果不是我不要继续。 然而,这不符合UICollectionView工作,反正它是没有效率的。 我正在考虑迁移到使用NSOperations
,它可以抵消,使得只有最后呼吁修改单元格会经过。 我可以通过检查操作已在完成可能做到这一点prepareForReuse
方法,如果不是取消它。 我希望有人已经处理了这个问题,在过去,并能提供一些提示或解决方案。
会话211 - iOS上构建并发用户界面,从2012年WWDC ,讨论了小区的形象改变,因为后台任务赶上(起始于38m15s)的问题。
这里是你如何解决这个问题。 已加载背景排队的图像后,使用索引的路径来寻找当前单元格,如果有的话,那就是显示包含图像的项目。 如果你得到一个单元格,设置单元格的图像。 如果你为零,还有目前正显示项目,以便只是丢弃图像无细胞。
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
MyCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath];
cell.imageView.image = nil;
dispatch_async(myQueue, ^{
[self bg_loadImageForRowAtIndexPath:indexPath];
});
return cell;
}
- (void)bg_loadImageForRowAtIndexPath:(NSIndexPath *)indexPath {
// I assume you have a method that returns the path of the image file. That
// method must be safe to run on the background queue.
NSString *path = [self bg_pathOfImageForItemAtIndexPath:indexPath];
UIImage *image = [UIImage imageWithContentsOfFile:path];
// Make sure the image actually loads its data now.
[image CGImage];
dispatch_async(dispatch_get_main_queue(), ^{
[self setImage:image forItemAtIndexPath:indexPath];
});
}
- (void)setImage:(UIImage *)image forItemAtIndexPath:(NSIndexPath *)indexPath {
MyCell *cell = (MyCell *)[collectionView cellForItemAtIndexPath:indexPath];
// If cell is nil, the following line has no effect.
cell.imageView.image = image;
}
下面是减少“堆放”的后台任务加载该视图不再需要图像的简单方法。 给自己一个NSMutableSet
实例变量:
@implementation MyViewController {
dispatch_queue_t myQueue;
NSMutableSet *indexPathsNeedingImages;
}
当你需要加载图像,放在集内的小区的索引路径和调度,它有一个索引路径出了一套和加载其图像的任务:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
MyCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath];
cell.imageView.image = nil;
[self addIndexPathToImageLoadingQueue:indexPath];
return cell;
}
- (void)addIndexPathToImageLoadingQueue:(NSIndexPath *)indexPath {
if (!indexPathsNeedingImages) {
indexPathsNeedingImages = [NSMutableSet set];
}
[indexPathsNeedingImages addObject:indexPath];
dispatch_async(myQueue, ^{ [self bg_loadOneImage]; });
}
如果停止显示单元,请从集内的小区的索引路径:
- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath {
[indexPathsNeedingImages removeObject:indexPath];
}
要加载图像,您可以通过设置索引路径。 我们必须派遣回这个主队列,以避免在组中的竞争条件:
- (void)bg_loadOneImage {
__block NSIndexPath *indexPath;
dispatch_sync(dispatch_get_main_queue(), ^{
indexPath = [indexPathsNeedingImages anyObject];
if (indexPath) {
[indexPathsNeedingImages removeObject:indexPath];
}
}
if (indexPath) {
[self bg_loadImageForRowAtIndexPath:indexPath];
}
}
该bg_loadImageForRowAtIndexPath:
方法与上述相同。 但是,当我们真正在小区集中的形象,我们也希望从集合中删除单元格的索引路径:
- (void)setImage:(UIImage *)image forItemAtIndexPath:(NSIndexPath *)indexPath {
MyCell *cell = (MyCell *)[collectionView cellForItemAtIndexPath:indexPath];
// If cell is nil, the following line has no effect.
cell.imageView.image = image;
[indexPathsNeedingImages removeObject:indexPath];
}
你是否尝试过使用SDWebImage开源框架,从网络上下载图像和异步缓存他们的作品? 它的工作原理绝对伟大的UITableViews
,并应与良好UICollectionViews
。 你只需要使用setImage:withURL:
方法的UIImageView
扩展,它的魔力。
我有同样的问题,约400元的UITableView中,发现一个残酷的,但工作的解决方案:不要重复使用细胞! 取决于有多少和多大的图像,但实际上iOS版重新分配未使用的电池非常有效......还是那句话:是不是纯粹的将建议,但夫妇与异步加载是照明快速,无差错而不必处理复杂的同步逻辑。