This post presents an implementation of autosizing, full-width UICollectionViewCells in Swift. Following the method in this post will allow any UICollectionViewCell you display in your UICollectionView to automatically determine its height.

If you need support resizing cells on rotation, check out the followup post Rotation Support for Autosizing, Full-width UICollectionViewCells in Swift 4.

The focus of this post will be automatic cell sizing, so boilerplate UICollectionView code like cell registration and cell design will be skipped.

Determine Cell Width

The first step is to compute the meaning of full-width. For our purposes, this will be the bounds of the collection view minus the left and right contentInset. One implementation option is to extend UICollectionView:

extension UICollectionView {
    var widestCellWidth: CGFloat {
        let insets = contentInset.left + contentInset.right
        return bounds.width - insets
    }
}

You may have other considerations, like the sectionInset on a UICollectionViewFlowLayout instance, that you will need to factor into your definition of full-width.

Override SystemLayoutSizeFitting

The next step is to create a UICollectionViewCell subclass that can automatically compute its own size. To do so, create a subclass and override systemLayoutSizeFitting(targetSize:, withHorizontalFittingPriority:, verticalFittingPriority:).

class FullWidthCollectionViewCell: UICollectionViewCell {
    override func systemLayoutSizeFitting(
        _ targetSize: CGSize, 
        withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, 
        verticalFittingPriority: UILayoutPriority) -> CGSize {
        
        // Replace the height in the target size to
        // allow the cell to flexibly compute its height
        var targetSize = targetSize
        targetSize.height = CGFloat.greatestFiniteMagnitude
        
        // The .required horizontal fitting priority means
        // the desired cell width (targetSize.width) will be
        // preserved. However, the vertical fitting priority is
        // .fittingSizeLevel meaning the cell will find the 
        // height that best fits the content
        let size = super.systemLayoutSizeFitting(
            targetSize,
            withHorizontalFittingPriority: .required,
            verticalFittingPriority: .fittingSizeLevel
        )
        
        return size
    }
}

Any cells that I would want to automatically size I would now subclass from FullWidthCollectionViewCell.

Configure EstimateItemSize on the CollectionViewLayout

The last step is to set the estimatedItemSize on the collection view's collectionViewLayout. Setting estimatedItemSize will trigger the UICollectionView to call systemLayoutSizeFitting when displaying a cell, allowing the cell to return its exact size.

override func viewDidLoad() {
    super.viewDidLoad()
    // ... other configuration ...
    
    let layout = collectionView.collectionViewLayout
    if let flowLayout = layout as? UICollectionViewFlowLayout {
        flowLayout.estimatedItemSize = CGSize(
            width: collectionView.widestCellWidth,
            // Make the height a reasonable estimate to
            // ensure the scroll bar remains smooth
            height: 200
        )
    }
}

Autosizing, Full-width UICollectionViewCells

That’s it! With some additional boilerplate UICollectionView code you can achieve the following results:

Portrait UICollectionViewCell layouts on an iPhone 8 simulator

The cells used in the example were created using custom UICollectionViewCell subclass with UI using autolayout. When the cell is dequeued using dequeueReusableCell(withReuseIdentifier:, for :) the UILabels in the cell are updated with the appropriate text. No other work is needed for the cells to display at the right height.

As noted in the beginning of this post, there is additional code needed to support cells resizing on rotation. If your app needs rotation, check out Rotation Support for Autosizing, Full-width UICollectionViewCells in Swift.