I have a UICollectionView with a header of type UICollectionReusableView.
In it, I have a label whose length varies by user input.
This drove me absolutely crazy for about half a day. Here's what finally worked.
Make sure the labels in your header are set to be dynamically sizing, one line and wrapping
Embed your labels in a view. This will help with autosizing.
Make sure the constraints on your labels are finite. ex: A greater-than constraint from the bottom label to the reusable view will not work. See image above.
Add an outlet to your subclass for the view you embedded your labels in
class CustomHeader: UICollectionReusableView {
@IBOutlet weak var contentView: UIView!
}
Invalidate the initial layout
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
collectionView.collectionViewLayout.invalidateLayout()
}
Lay out the header to get the right size
extension YourViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
if let headerView = collectionView.visibleSupplementaryViews(ofKind: UICollectionElementKindSectionHeader).first as? CustomHeader {
// Layout to get the right dimensions
headerView.layoutIfNeeded()
// Automagically get the right height
let height = headerView.contentView.systemLayoutSizeFitting(UILayoutFittingExpandedSize).height
// return the correct size
return CGSize(width: collectionView.frame.width, height: height)
}
// You need this because this delegate method will run at least
// once before the header is available for sizing.
// Returning zero will stop the delegate from trying to get a supplementary view
return CGSize(width: 1, height: 1)
}
}