How to get a snappy UICollectionView with lots of images (50-200)?

吃可爱长大的小学妹 提交于 2019-12-02 18:27:13

I think that approach #3 is the best way to go, and I think I may have spotted the bug.

You're assigning to @image, a private variable for the whole collection view class, in your operation block. You should probably change:

@image = UIImage.imageWithContentsOfFile(image_path)

to

image = UIImage.imageWithContentsOfFile(image_path)

And change all the references for @image to use the local variable. I'm willing to bet that the problem is that every time you create an operation block and assign to the instance variable, you are replacing what is already there. Due to some of the randomness of how the operation blocks are dequeued, the main queue async callback is getting the same image back because it is accessing the last time that @image was assigned.

In essence, @image acts like a global variable for the operation and async callback blocks.

The best way I found of solving this problem was by keeping my own cache of images, and pre-warming it on viewWillAppear on a background thread, like so:

- (void) warmThubnailCache
{
    if (self.isWarmingThumbCache) {
        return;
    }

    if ([NSThread isMainThread])
    {
        // do it on a background thread
        [NSThread detachNewThreadSelector:@selector(warmThubnailCache) toTarget:self withObject:nil];
    }
    else
    {
        self.isWarmingThumbCache = YES;
        NIImageMemoryCache *thumbCache = self.thumbnailImageCache;
        for (GalleryImage *galleryImage in _galleryImages) {
            NSString *cacheKey = galleryImage.thumbImageName;
            UIImage *thumbnail = [thumbCache objectWithName:cacheKey];

            if (thumbnail == nil) {
                // populate cache
                thumbnail = [UIImage imageWithContentsOfFile:galleryImage.thumbImageName];

                [thumbCache storeObject:thumbnail withName:cacheKey];
            }
        }
        self.isWarmingThumbCache = NO;
    }
}

You can also use GCD instead of NSThread, but I opted for NSThread, since only one of these should run at a time, so no queue is necessary. Also, if you have an essentially limitless number of images, you will have to be more careful than I. You will have to be more clever about loading the images around a user's scroll location, rather than all of them, like I do here. I can do this because the number of images, for me, is fixed.

I should also add that your collectionView:cellForItemAtIndexPath: should use your cache, like so:

- (PSUICollectionViewCell *)collectionView:(PSUICollectionView *)collectionView
                  cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"GalleryCell";

    GalleryCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CellIdentifier
                                                                                forIndexPath:indexPath];

    GalleryImage *galleryImage = [_galleryManager.galleryImages objectAtIndex:indexPath.row];

    // use cached images first
    NIImageMemoryCache *thumbCache = self.galleryManager.thumbnailImageCache;
    NSString *cacheKey = galleryImage.thumbImageName;
    UIImage *thumbnail = [thumbCache objectWithName:cacheKey];

    if (thumbnail != nil) {
        cell.imageView.image = thumbnail;
    } else {
        UIImage *image = [UIImage imageWithContentsOfFile:galleryImage.thumbImageName];

        cell.imageView.image = image;

        // store image in cache
        [thumbCache storeObject:image withName:cacheKey];
    }

    return cell;
}

Make sure your cache clears when you get a memory warning, or use something like NSCache. I'm using a framework called Nimbus, which has something called NIImageMemoryCache, which lets me set a max number of pixels to cache. Quite handy.

Other than that, one gotcha to take note is is to NOT use UIImage's "imageNamed" method, which does it's own caching, and will give you memory problems. Use "imageWithContentsOfFile" instead.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!