Did you get a chance to watch the WWDC 2016 session, “What’s New in UICollectionView in iOS10″ yet? I watched it today. There is some good stuff in the session, and I want to recap it for you in this post. The session is broken into three segments that capture everything that’s new for an iOS 10 UICollectionView :
Improvements to:
- Smooth Scrolling
- Self-Sizing Cells
- Interactive Reordering
There is a special bonus fourth segment as well, but I’ll save that for later.
Smooth Scrolling Enhancements
In iOS 10, there are several enhancements that will improve the performance of your UICollectionViews
– some of which you will manually need to leverage, and some you will get for free. Before dropping into the new features of iOS 10 UICollectionView, the presenter gives a nice overview of what it means to “drop frames” and what it’s bad.
Don’t Drop Frames
In order for your app to have “buttery smooth” performance, a hallmark of iOS apps, you must strive for app animation that performs at 60 frames per second. This means that a given frame of the user interface must be displayed in less than 16.67ms in order for the animation to appear “smooth.” Otherwise, when the frame rate drops lower than that, it’s apparent to the user in the form of a choppy animation. The easiest way to make the frame rate drop, is to do things like add blocking, long running method calls on the main thread in the middle of your animation. Here’s an in-depth article from Facebook on how they measure and ensure a highly performant news feed in their app.
Less Aggressive Loading and Unloading Cells
In a UICollectionView
, the lifecycle of a cell is as follows:
prepareForReuse
cellForItemAtIndexPath
– The heavy lifting of populating your cell’s data from your model happens here.
willDisplayCell
– Lightweight preparation for when you cell is about to go onscreen.
- Cell comes on screen, and as scrolling continues starts to move offscreen.
didEndDisplayingCell
In general, this flow is unchanged between iOS 9 and iOS 10. The different is when these methods are called. Apple has optimized when willDisplayCell
is called. In iOS 10, it’s now called at the very last minute before the cell goes on screen. This helps to balance out the CPU performance in drawing cells, but not executing that code too early. Additionally, another enhancement Apple has made in iOS 10 UICollectionView is that cells are not put on the reuse queue as aggressively as in the past. Instead, after a cell leaves the screen, it is kept around a little longer in case the user decides to swipe the cell back on screen.
Cell Pre-fetching
Apple also enhanced UICollectionView
such that by default, cells are pre-fetched. This means that you can get even earlier awareness of when data for a cell is needed such that you can retrieve it. For example, if you are building a UICollectionView
full of remote images. Leveraging the UICollectionViewDataSourcePrefetching
, UIKit will call:
collectionView(_:prefetchItemsAt:)
to allow for you to start downloading the images with Grand Central Dispatch of an NSOperationQueue
such that when the cells containing the images comes on screen, the images will be downloaded and ready to go.
If you need to, you can opt out of this behavior by setting isPrefetchingEnabled
to false
, but why would you?
As part of pre-fetching, realizing that cellForItemAtIndexPath
may be called for cells that never end up coming on screen – because the user stopped scrolling before they were shown. Also, it’s really important that you keep the work in willDisplayCell
and didEndDisplayingCell
really light. All the heavy lifting goes in cellForItemAtIndexPath
. Apple described pre-fetching as an “adaptive technology” which I assume to mean that it’s level of “predictiveness” varies by use case for a given application.
And as bonus, this exact same pre-fetching API is also available on UITableView
via UITableViewDataSourcePrefetching
.
Self Sizing Enhancements
Prior to iOS 10, estimatedItemSize
has existed on UICollectionViewFlowLayout
in order for you to provide an estimate size for the items in your collection view. Sometimes it’s hard to predict the size of items in your UICollectionView
. Realizing this, Apple has introduced automatic item sizing for your UICollectionViewFlowLayout
. Simple set the item size to the constant UICollectionViewFlowLayoutAutomaticSize
and UIKit will make smart guesses based on past measurements of your items in order to automatically predict item sizes for future items.
According to Apple:
It will keep a running tally of all the cells it’s already sized, and use that to influence its future sizing estimates…making the sizing much more accurate…leading to better performance and a more accurate layout.
Interactive Reordering Enhancements
Reordering a iOS 10 UICollectionView has also undergone some improvements as well. Prior to iOS 10, if you didn’t already know (and I only learned recently), it’s really easy to enable reordering on your UICollectionView
– in your UICollectionViewDelegate
, simply implement:
func collectionView(_ collectionView: UICollectionView, targetIndexPathForMoveFromItemAt originalIndexPath: IndexPath, toProposedIndexPath proposedIndexPath: IndexPath) -> IndexPath
There are some additional methods on UICollectionView
that you should take a peak at too that enable you to add more advanced animation and update your data model if appropriate.
New in iOS 10 UICollectionView is the ability to reorder with paging!
collectionView.isPagingEnabled = true
That’s it! The presenter describes it as an interaction that feels just like moving icons between pages on your home screen.
The Bonus
Finally, the big reveal happens. It’s as if this is such exciting news worthy of a WWDC reveal, but there is no other session appropriate for it. The presenters reveal that pull to refresh will be supported on:
UIScrollView
UITableView
UICollectionView
If it wasn’t awesome enough that UIScrollView
and UICollectionView
got the control, but you are also no longer constrained to needing a UITableViewController
if you want an out of the box pull to refresh control (which was a pretty annoying prior limitation in my opinion).
Final Thoughts
I have plans to do an in-depth example of how to use pre-fetching with Grand Central Dispatch in order load remote images in a UITableView
sometime in the future. I recently ran into a problem in one of my apps that this exact thing would have solved. Essentially I had cells in a UITableView
that were of varied height based on a remote UIImage
being loaded. I ended up needing to set a static height on the cells to achieve a high frame rate and scaling the images, instead of properly sizing the cells according to the natural height of the image. It wasn’t the end of the world, but there was some extra white space in the cells that wasn’t needed. How do you plan on using these new changes to iOS 10 UICollectionView?
Happy cleaning.