This post presents an implementation of autosizing (automatic height), full-width UICollectionViewCell
s in Swift. Following the method in this post will allow any UICollectionViewCell
you display in your UICollectionView
to automatically determine height.
- Compute Full, Fixed Width
- Override SystemLayoutSizeFitting To Compute Dynamic Height
- Configure EstimateItemSize on the CollectionViewLayout For Autosizing Cells
- 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:
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 UILabel
s 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.