iphone

At WWDC 2016 Apple announced a newly revamped UICollectionView class for iOS 10 with smoother scrolling, self-sizing cells, and interactive cell reordering. In this blog post I want to look more closely at the scrolling enhancements and changes to the UICollectionView classes, and more specifically, how to implement them in iOS 10.

During the conference presentation, the Apple engineers explained what causes a poor scrolling experience in a collection view - dropped animation frames. In order for your app to have smooth scrolling performance, the scrolling animation must perform at a minimum of 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. If and when the frame rate drops lower than that, the result is choppy and uneven animation. And while much of the work in creating a great scrolling experience is with the developer, Apple has recognized some areas in the UICollectionView classes that could be improved for iOS 10.

Let's begin by looking at what's changed.

The UICollectionView Life Cycle

The collection view life cycle includes a series of delegate methods that are called in a specific order as a collection view cell is created, displayed, and queued. This has not changed in iOS 10, but the willDisplayCell method is now called by the UICollectionView class just before the cell actually displays in the view. This small change helps avoid performance bottlenecks as the collection view won't try draw a cell before it really needs to.

Further, the collection view classes have been changed to be less aggressive when putting a cell in the reuse queue. In iOS 10 the presented cells are kept around a little longer even after they leave the screen. The small change allows the collection view to avoid the overhead of recreating the cells (or dequeuing them) if the scrolling action suddenly changes direction and the cells need to redisplay.

Prefetching

One of the best performance enhancements announced for UICollectionView in iOS 10 is a new feature called prefetching.

Prefetching is a mechanism by which you are notified early before the collection view will need the cells to be displayed, thus providing an opportunity to prepare the cell's data source in advance of actually creating the cell.

Prefetching is provided by the UICollectionViewDataSourcePrefetching protocol and contains two instance functions:

func collectionView(UICollectionView, prefetchItemsAt: [IndexPath]) // required
func collectionView(UICollectionView, cancelPrefetchingForItemsAt: [IndexPath]) // optional

The collectionView(_:prefetchItemsAt:) function is called when the collection view is ready to start building out cells even before they are on the screen. An array of NSIndexPath objects is passed to the function and is used to prepare your data source.

For instance, if you have a data source called dataSourceArray that is used to populate your cells, you could do something like the following:

func collectionView(_ collectionView: UICollectionView, prefetchItemsAt indexPaths: [IndexPath]) {
 for indexPath in indexPaths {
 // calculate/update/collect some data
 dataSourceArray[indexPath.row] = updatedData
 }
}

Now your data source is ready to be consumed when the cellForItemAtIndexPath function is called later by the collection view.

A few things to note when using prefetching:

  • cellForItemAtIndexPath may be called for cells that never actually end up in the view because the user scrolled back the view before they could show.
  • The prefetchItemsAt function only provides an opportunity to update the underlying data source for your collection view cells, not the cells themselves (i.e. you don't return any data to the collection view)
  • Apple recommends keeping the willDisplayCell and didEndDisplayingCell functions relatively light and placing the bulk of your cell work in the cellForItemAtIndexPath function.
  • The prefetching feature is an "adaptive technology" and will automatically vary when and how often the prefetchItemsAt function is called. In fact, at faster scrolling rates the prefetchItemsAt function can and may disable itself entirely.

As mentioned above, the cancelPrefetchingForItemsAt function is an optional feature that allows you to revert or clean up your data source when prefetching for an array of cells has been cancelled by the collection view. This can happen when scrolling changes direction or becomes too rapid for prefetching to be implemented effectively.

Finally, there may be times when you want to disable collection view prefetching. This is can be done by setting the UICollectionView isPrefetchingEnabled property to false.

As an aside, Apple has also provided prefetching for the UITableView classes with the new UITableViewDataSourcePrefetching protocol.

The UICollectionView classes for iOS 10 promise better performance with the under-the-hood changes like prefetching and life cycle function improvements. As we've seen above, prefetching is easy to implement and many of the performance tweaks work right out of the box without any changes to existing code.