How to create sticky headers with Compositional Layout
It is actually quite easy, but you need to know where to activate this behavior. Let's see how to enable pinned headers.
Published: Feb. 9, 2021 Sponsored App StoreIf you worked with TableView and recently switched to CollectionView, you may miss the automatic sticky section headers that TableView provided in the default configuration. These sticky / pinned headers are useful when presenting longer lists, because they never go out of view and user can always see what section are they currently viewing.
This post is going to be short, because creating this sort of headers is very straightforward with Compositional Layout.
If you want your header to be sticky, you need to set this on the header element itself. It is the pinToVisibleBounds
property.
So in practice it looks like this:
let headerSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(44))
let headerElement = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: headerSize, elementKind: UICollectionView.elementKindSectionHeader, alignment: .top)
// this activates the "sticky" behavior
headerElement.pinToVisibleBounds = true
And done! Quite nice, right? There are few possible gotchas you need to watch out for. For one, make sure the header has solid background color, otherwise user will think there is a bug when your header covers the items. And also you cannot have top inset set on the collection view with the contentInset
property.
Header views setup recap
Let's also briefly recap all the pieces you need to display headers (sticky or not) with Compositional Layout. Above we have defined the headerElement
. The next step is to assign it to the section definition:
section.boundarySupplementaryItems = [headerElement]
The section
is the NSCollectionLayoutSection
type.
Next you need a method (or inline closure) that will be responsible for dequeuing the correct header element. Basic example looks like this:
func supplementary(collectionView: UICollectionView, kind: String, indexPath: IndexPath) -> UICollectionReusableView? {
let header = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: StickyHeaderView.reuseIdentifier, for: indexPath) as! StickyHeaderView
header.configure(with: "Sticky header \(indexPath.section + 1)")
return header
}
And the last step is to configure the Diffable Data Source:
datasource.supplementaryViewProvider = { [unowned self] collectionView, kind, indexPath in
return self.supplementary(collectionView: collectionView, kind: kind, indexPath: indexPath)
}
I have written previously about using these methods instead of closures and also about useful extensions which you can then use to register the header class with your collection view like this:
collectionView.register(header: StickyHeaderView.self)
And thats all the steps required. The complete code is available on GitHub.