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

  1. Compute Full, Fixed Width
  2. Override SystemLayoutSizeFitting To Compute Dynamic Height
  3. Configure EstimateItemSize on the CollectionViewLayout For Autosizing Cells
  4. Automatic Height, Full-width UICollectionViewCells

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 dynamic cell sizing, so boilerplate UICollectionView code like cell registration and cell design will not be reviewed.

Compute Full, Fixed Width

The first step is to compute the full-width of the cell to use as the fixed width for all cells in this collection view. For our purposes, this will be the bounds of the collection view minus the left and right contentInset. If the collection view frame spans the screen, widestCellWidth will be equivalent to a full screen cell. 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 To Compute Dynamic Height

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 For Autosizing Cells

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
        )
    }
}

Automatic Height, 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
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 UICollectionViewCells in Swift.