I am looking for the best way to use auto layout to calculate the size of supplementary views in a custom UICollectionViewLayout
. For the purposes of clarity this is a simplified explanation of what I am trying to achieve.
I have a header supplementary view ACVHeaderView
with auto layout constraints.
In prepareLayout:
I want to calculate the size of the section header and create supplementary view layout attributes for it. Then I want to iterate through the cells in that section, creating and storing layout attributes for the cells. Having 'built' the layout these attributes will all be stored to be returned by the other collection view layout methods.
Ideally I want to size the header by calling collectionView:dequeueReusableSupplementaryViewOfKind:withReuseIdentifier:forIndexPath:
, perform an auto-layout pass and then use view:systemLayoutSizeFittingSize:
. This is exactly what I do for the cells themselves (following some example code I got from WWDC).
However this fails with this error:
2014-09-04 18:51:53.852 ACV[7298:167827] *** Assertion failure in -[UICollectionViewData layoutAttributesForSupplementaryElementOfKind:atIndexPath:], /SourceCache/UIKit_Sim/UIKit-3302.3.1/UICollectionViewData.m:853
2014-09-04 18:51:53.916 ACV[7298:167827] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'request for layout attributes for supplementary view UICollectionElementKindSectionHeader in section 0 when there are only 0 sections in the collection view'
I understand the problem I think... I am trying to dequeue a supplementary view for the header before I have sized any cells in that section. That means I have not yet created any layout attributes for any cells in that section (using UICollectionViewLayoutAttributes.forCellWithIndexPath:
). UICollectionViewData
looks like an internal class that tracks layout attributes in a UICollectionView
and it won't allow me to dequeue a supplementary view before there are layout attributes for at least one cell.
I have thought of the following:
- I could layout the cells first and then the header. However, that makes the logic for setting the cell frames more complicated because they depend on the height of the header.
- I could create a 'dummy' attributes for a cell, create and size the header, then update the dummy attributes when I layout the first real cell. This is not difficult but neither is it very clean!
- I could manually create an instance of
ACVHeaderView
to use for the purpose of sizing headers. That would work, but in my real-world case I have multiple supplementary views. It seems untidy to have to keep my own record of supplementary kinds and reuse identifiers and a dictionary of prototype views. I would be duplicating the logic used in the collection view dequeuing mechanism.
What is the best way to achieve this? I imagine I must be missing something more straightforward because Apple are encouraging developers to make proper use of dynamic type and auto-layout.