VButko

How to enable self-sizing cells in UICollectionViewCompositionalLayout

UICollectionViewCompositionalLayout is a highly adaptive and modern way of building UICollectionView layout. We provide dimensions that can be absolute, estimated, or fractional. If you want your cells to have self-sizing behavior you should use estimated size when you configure your Item's size:

let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(250))

Incorrect Approach

Imagine that we want to create a section that will have only one cell with a flexible height. As our Item should be the same size as the Group, we may decide to express this in such way:

  • Set Item size to have a fractional dimension of the Group
  • Set Group size to have an estimated height

However, this won't work as expected, the cell won't change its size following the content size. This is because of the hierarchical structure of the UICollectionViewCompositionalLayout.

Layout hierarchy

UICollectionViewCompositionalLayout has a hierarchical structure with Items, Groups, and Sections:

When you are constructing NSCollectionLayoutSectin, you start with configuring each Item's layout, then Groups that contain Items. Finally, you provide configured Group to create a Section layout.

When you configure the sizes of each element, it's important to keep in mind the hierarchy.

Because Items are the first building blocks, the self-sizing behavior using an estimated value for a dimension should be activated on them. To illustrate this, let's look at the code.

Incorrect approach. Cell's won't use their content's height:

let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractinalHeight(1.0))
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(250))

The correct way is to use estimated height dimension for both:

let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(250))
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(250))

The final code for the NSCollectionLayoutSection will look like this:

func createOverviewSection() -> NSCollectionLayoutSection { 
    let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(250))
		let item = NSCollectionLayoutItem(layoutSize: itemSize)

		let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(250))
    let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
  
    let section = NSCollectionLayoutSection(group: group)    
		return section
}

Conclusion

Fortinatelly, we have such a great and flexible solution to make our layouts as UICompositionalLayout. Understanding the building blocks and size management is very important to achieve the desired result. I hope you enjoyed this article. If you have any questions, suggestions, or feedback, please let me know on Twitter.

Thanks for reading!