Implementing UICollectionViewLayout

19 February 2013

There are a lot of resources available about UICollectionView since the release in iOS 6.0. But most of them use the somewhat basic layout features with UICollectionViewDelegateFlowLayout. It for example only allows you to scroll horizontally or vertically. But what if you want both? What if you have an advanced and dynamic layout? That's where UICollectionViewLayout comes in to play.

This is a short guide to help you understand UICollectionViewLayout. I suggest you check out UICollectionViewWaterfallLayout on GitHub after reading this. It covers all the basics that you'll need to get started with UICollectionViewLayout.

I've also created a sample Xcode project (see screenshot above) that implements UICollectionViewLayout. Get it here.

UICollectionViewLayout

UICollectionViewLayout has a different flow compared to UICollectionViewDelegateFlowLayout, which is similar to UITableViewDelegate where you for example provide heights for the cells and more.

You need to provide the position and the size for each item when you use UICollectionViewLayout. You do this by providing UICollectionViewLayoutAttributes inside UICollectionViewLayout for each item in your collection view.

UICollectionViewLayoutAttributes

An UICollectionViewLayoutAttributes object manages the layout-related attributes for a given item in a collection view. Layout objects create instances of this class when asked to do so by the collection view. In turn, the collection view uses the layout information to position cells and supplementary views inside its bounds.

UICollectionViewLayoutAttributes is the only object that determines the size and position of the cell. You should not confuse this with your UICollectionViewDataSource which should only set the content for the cell. It's important to separate these two.

Implementation of UICollectionViewLayout

The first thing you'll need to do is to subclass UICollectionViewLayout and connect it with your collection view. You can do this in UICollectionView's init method initWithFrame:collectionViewLayout.

To get the basics to work you need to at least override these three methods in your UICollectionViewLayout subclass:

collectionViewContentSize

layoutAttributesForElementsInRect:

layoutAttributesForItemAtIndexPath:

You return the contentSize in the first method and the UICollectionViewLayoutAttributes in the last two methods. You could calculate the attributes for an item 'on-the-fly' but UICollectionViewLayout comes with a method called prepareLayout where it gives you a chance to pre-calculate the attributes for all items inside the collection view.

prepareLayout

UICollectionView will call prepareLayout when the collection view presents itself for the first time and each time the layout is invalidated (you can manually invalidate the layout by calling 'invalidateLayout' on the UICollectionViewLayout).

During each layout update, the collection view calls this method first to give your layout object a chance to prepare for the upcoming layout operation.

You should use prepareLayout to calculate the attributes for all your items and store these calculated attributes somewhere in your UICollectionViewLayout subclass so you can return the attributes when UICollectionView calls layoutAttributesForElementsInRect: or layoutAttributesForItemAtIndexPath:.

Horizontal and vertical scrolling

During the implementation of UICollectionViewLayout you are entirely responsible for the contentSize of the UICollectionView. Since you are pre-calculating all the attributes in prepareLayout you also know how big the content size will be. Simply provide a width and height that's larger than the UICollectionView bounds to enable both horizontal and vertical scrolling. There's an example in my example project.