iOS - 修复 cellForItemAtIndexPath 图片加载问题

iOS - Fix cellForItemAtIndexPath image loading issue

我正在构建一个包含大量要在集合视图中显示的大图像文件的应用程序。由于图像的大小,我发现从它们的 URL 创建缩略图以及使用图像缓存要快得多。当我第一次使用 GCD 在 cellForItemAtIndexPath 中实现它时,我发现 UI 延迟大大减少,但我也注意到当集合视图进入视图并滚动时,单元格中的图像会闪烁并快速变化。我发现了一些关于类似问题的其他帖子,他们说首先检查单元格是否为 nil 应该可以解决这个问题,但不幸的是,这似乎会产生另一个问题,其中许多图像永远不会加载。有谁知道如何解决这个问题?

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
PhotoCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
ObjectWithPhoto *object = self.objects[indexPath.item];
cell.imageView.image = nil;

NSString *imageName = object.imageName;
NSString *imageKey = [NSString stringWithFormat:@"%@_thumbnail", imageName];
if ([[ImageCache sharedCache] imageForKey:imageKey]) {
    cell.imageView.image = [[ImageCache sharedCache] imageForKey:imageKey];
} else {
    dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
        NSURL *imageURL = [[NSBundle mainBundle] URLForResource:imageName withExtension:@"jpg"];
        CGSize imageSize = CGSizeMake(self.view.frame.size.width, self.view.frame.size.width);
        UIImage *thumbnail = [UIImage createThumbnailFromURL:imageURL imageSize:imageSize];
        [[ImageCache sharedCache] setImage:thumbnail forKey:imageKey];
        dispatch_async(dispatch_get_main_queue(), ^(void) {
            PhotoCell *cellToUpdate = (id)[collectionView cellForItemAtIndexPath:indexPath];
            if (cellToUpdate) {
                cellToUpdate.imageView.image = thumbnail;
            } else {
                NSLog(@"cell is no long visible");
            }
        });
    });
}
return cell;
}

我想我找到了解决办法。当检查单元格是否仍然可用或为零时,它似乎 return 零,即使我发誓我仍然可以在屏幕上看到它。考虑到这一点,我尝试告诉集合视图在单元格返回 nil 时重新加载数据并且它有效!流畅的滚动和全无闪烁的图像。

PhotoCell *cellToUpdate = (id)[self.collectionView cellForItemAtIndexPath:indexPath];
if (cellToUpdate) {
    cellToUpdate.imageView.image = thumbnail;
} else {
    NSLog(@"cell is no long visible");
    [self.collectionView reloadData];
}

也许您对您的解决方案感到满意,但我不满意。我认为您看到的至少一个怪异来源是在您需要的图像未缓存的情况下不清除图像(或将其设置为占位符)。请记住,一旦您开始滚动,图像就不会在缓存中,但重复使用的单元格的图像将被设置为其他 indexPaths 的图像,这是错误的。所以,修复一个...

if ([[ImageCache sharedCache] imageForKey:imageKey]) {
    cell.imageView.image = [[ImageCache sharedCache] imageForKey:imageKey];
} else {
    // fix one: clear the cell's image now, if it's set, it's wrong...
    cell.imageView.image = nil; // or a placeholder
    dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
        // ... 

其次,每当我看到有人调用他们自己的 cellForItem 数据源方法并将值插入单元格时,我都会发出黄色标记。这样更简洁更有礼貌....

cell.imageView.image = nil; // or a placeholder
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
    NSURL *imageURL = [[NSBundle mainBundle] URLForResource:imageName withExtension:@"jpg"];
    CGSize imageSize = CGSizeMake(self.view.frame.size.width, self.view.frame.size.width);
    UIImage *thumbnail = [UIImage createThumbnailFromURL:imageURL imageSize:imageSize];
    [[ImageCache sharedCache] setImage:thumbnail forKey:imageKey];
    dispatch_async(dispatch_get_main_queue(), ^(void) {
        // fix two: don't get the cell.  we know the index path, reload it!
        [collectionView reloadItemsAtIndexPaths:@[indexPath]];
        // deleted evil stuff that was here
    });
});