Additional tips on improving scrolling performance of a UICollectionView

Problem and Learnings

Recently I spent some time optimizing the scrolling performance of a UICollectionView so that scrolling is now 60 fps (frames per sec) and buttery smooth (iPhone 5/6/6s) I followed some hints on a Ray Wenderlich tutorial which links to a very good WWDC 2012 video on iOS app performance: graphics & animations. If you happen to run into these issues you will find these videos/articles quite useful. We were observing janky behavior on the main landing view of the WeWork iOS app that is a UICollectionView. The jankiness during scrolling was clearly visible and here are the frame rates that we were observing before any optimizations.

![FPS before][1] [1]: http://res.cloudinary.com/wework/image/upload/v1450306055/engineering/Screen_Shot_2015-12-04_at_9.52.09_AM.png

In our case after running the Core Animation intrument and after baselining performance, it turned out the issue was GPU bound and the renderer was doing a lot of work. The scrolling was visibly jittery and the Core Animation instrument showed 45 - 55 fps while scrolling on the collection view. After using the simulator and Core Animation instrument to enable color blended layers (CA) it was clear that there were a number of CA layers being rendered. Often in such cases UIImages can be the bottleneck. Either the images are being loaded without using imageNamed or the resolution of the image is too high e.g. a large image being loaded into a thumbnail view.

What is interesting in the video is that the Apple engineer recommends using the platform’s built in caching support, async drawing support & flattening support before pulling out the heavy tools. It is also important to make sure one is reusing cells etc. In this case eliminating blending of the layers in the various subviews of the UICollectionViewCell gave the most bang for the buck and was a solution we could live with. The general methodology is to determine if it is a CPU or GPU bound issue using the CA Instrument. Come up with a theory and baseline, then make code changes and measure again. See if there is improvement and record all measurements. The WWDC video recommends some other tips worth checking out and speculative caching is another powerful technique.

You might want to check out NSCache/NSPurgeable especially for images. Facebook’s AsyncDisplayKit might be worth checking out especially if one is developing an app from scratch (we did not use it). Here are the frame rates after blending all the CA layers in the collection view cells.

![FPS after][2] [2]: http://res.cloudinary.com/wework/image/upload/v1450306072/engineering/Screen_Shot_2015-12-07_at_3.51.58_PM.png

Additional Tips

To add Re: image rendering, in addition to Debug > Color Blended Layers, you can use Debug > Color Misaligned Images to show scaled images in yellow and misaligned images in magenta. The best bet for fast scrolling is no blending, no scaled images, no misaligned images, no shadows modulo what you can live with respect to design.

To improve the thumbnail insertion if you have significant number of thumbnail images, consider using NSCache/NSPurgeable for the thumbnail images. In other words, create the thumbnail image on the fly and store the result in a cache (speculative caching) based on a key using the cell or record ID. Then the next time the image is needed, rather than recalculating/resizing you re-use the cached image. If it has been purged, you recalculate. This is pretty straightforward and can be used for all scrolling which has a number of images which are expensive to either generate or retrieve. It is also what the Apple home screen uses for all the App Icons.

There is a WWDC talk Re: how Apple implemented the home screen scrolling and all of the parameters they had to consider such as cache size, memory usage vs cache usage. It is an excellent talk for understanding NSCache/NSPurgeable.

Written by Amit Rao
Principal mobile engineer (iOS) and open-source civic hacker.

Related Posts 10

How To Scale As A CTO - Why Course Correction Is Inefficient (Video)
Bridging digital and physical with a MakerBot Replicator 2
How I Aced my Technical Interview (by pretending to be a wizard)
Why WeWork.com uses a static generator and why you should too
Rabbits, Bunnies and Threads
Adding columns with default values to really large tables in Postgres + Rails
Creating BEM Stylesheets for React Components
React Tutorial - Converting Reflux to Redux
Inside WeWork's Tech Stack
Caching External APIs in Rails for a Ginormous Speed Boost