How to improve performance of UCollectionView cont

2019-03-17 22:16发布

In my iOS app I have UICollectionView that displays around 1200 small (35x35 points) images. The images are stored in application bundle.

I am correctly reusing UICollectionViewCells but still have performance problems that vary depending on how I address image loading:

  • My app is application extension and those have limited memory (40 MB in this case). Putting all 1200 images to Assets catalog and loading them using UIImage(named: "imageName") resulted in memory crashes - system cached images which filled up the memory. At some point the app needs to allocate bigger portions of memory but these were not available because of cached images. Instead of triggering memory warning and cleaning the cache, operating system just killed the app.

  • I changed the approach to avoid images caching. I put images to my project (not to asssets catalog) as png files and I am loading them using NSBundle.mainBundle().pathForResource("imageName", ofType: "png") now. The app no longer crashes due to memory error but loading of single image takes much longer and fast scrolling is lagging even on the newest iPhones.

I have full controll over the images and can transform them for example to .jpeg or optimize them (I already tried ImageOptim and some other options without success).

How can I resolve both these performance problems at once?


EDIT 1:

I also tried loading images in background thread. This is code from my subclass of UICollectionViewCell:

private func loadImageNamed(name: String) {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), { [weak self] () in
        let image = bundle.pathForResource(name, ofType: "png")?.CGImage
        if name == self?.displayedImageName {
            dispatch_async(dispatch_get_main_queue(), {
                if name == self?.displayedImageName {
                    self?.contentView.layer.contents = image
                }
            })
        }
    })
}

This makes scrolling smooth without consuming additional memory for caching but when scrolling to some location programatically (for example when UICollectionView scrolls to top) it causes another problem: During scrolling animation the images do not update (scroll is too fast for them to load) and after scrolling is finished it takes wrong images are displayed for fraction of second - and one after another replaced with correct ones. This is very disturbing visually.


EDIT 2:

I cannot group small images into bigger composed images and display those as suggested by this answer.

Reasons:

  • Consider different screen sizes and orientations. There would have to be precomposed images for each of them which would make the app download huge.
  • The small images can by displayed in different order, some of them might be hidden in some situation. I surrely cannot have precomposed images for each possible combinations and orders.

7条回答
Juvenile、少年°
2楼-- · 2019-03-17 22:39

You should create an queue to load image asynchronously. The best choice is a last in first out queue. You can have look at this LIFOOperationQueue. One important thing is prevent showing wrong image that need to be handle separately. To do that, when you create an operation to load the image, give it the current indexPath as identifier. And then in callback function, check if the given indexPath is visible to update the view

if (self.tableView.visibleIndexPath().containsObject(indexPath) {
    cell.imageView.image = img;
}

You should also need to custom the LIFOOperationQueue to have maximum number of task in queue so that it can remove unnecessary task. It is good to set the maximum number of task is 1.5 * numberOfVisibleCell.

One last thing, you should create load image operation in willDisplayCell instead of cellForRowAtIndexPath

查看更多
登录 后发表回答