How to give a collection view section header dynamic height
None of the answers given so far are good practices since calculating the size manually or by rendering a label offscreen are both inefficient and violate the single source of truth principle.
Instances of UICollectionReusableView
have the preferredLayoutAttributesFitting()
method. When the cell becomes displayed, this method will be called. In this method, if you use Auto Layout, you ask the whole cell for its systemLayoutSizeFitting()
and then modify the attributes. The layout will then be responsible to apply them via layout invalidation.
How can I dynamically resize a header view in a UICollectionView?
This is how I solved it. I wish it was more elegant.
- Set the
UILabel
in the header view to have anumberOfLines
value of 0. This will let it resize itself. - Move the header out of the storyboard and into a xib.
- In
collectionView(_:layout:referenceSizeForHeaderInSection:)
instantiate a header from the xib. We'll use it to compute a size. - Set the header to the desired width (in my case the width of the collection view), and configure it with the desired text and images
- Call
setNeedsLayout
andlayoutIfNeeded
. - Use
systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
to compute the height. - Return the height and width as CGSize
Notes:
The header must be moved out of the storyboard and into a xib because calling
dequeuReusableSupplementaryViewOfKind
fromsystemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
causes a crash.The header view must be able to compute a correct height only knowing its content and the desired width. This took some fiddling with the constraints.
Self-sizing UICollectionView with UITableView with dynamic header as a cell
To implement auto-sizing collectionView cells, you really only need a few changes.
Couple key points:
Cells must satisfy their own constraints. So, instead of calculating sizing in
sizeForItemAt
, make sure the cells have width and height constraints. These can be dynamic, based on content.Add elements to the collection view cell's
contentView
, not to the cell itself.For the embedded non-scrolling table view, use a subclass that sets the intrinsic content size height based on the table's contentSize. Example:
final class ContentSizedTableView: UITableView {
override var contentSize:CGSize {
didSet {
invalidateIntrinsicContentSize()
}
}
override var intrinsicContentSize: CGSize {
layoutIfNeeded()
return CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height)
}
}
There is a pretty good tutorial here (not mine): https://medium.com/@andrea.toso/uicollectionviewcell-dynamic-height-swift-b099b28ddd23 that has more detailed explanations.
I implement those concepts in the project you made available, and put it up on a GitHub repo (to make it easy to see the changes): https://github.com/DonMag/ThunderCollectionView
Results:
How to create UICollectionView section Header which autosizes to label content + font size change?
Here is one approach:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
// Get the view for the first header
let indexPath = IndexPath(row: 0, section: section)
let headerView = self.collectionView(collectionView, viewForSupplementaryElementOfKind: UICollectionView.elementKindSectionHeader, at: indexPath) as! SectionHeaderView
// appearance is not applied until view is added to the view hierarchy, so
// get the UIAppearance protocol for UILabel contained in SectionHeaderView class
let a = UILabel.appearance(whenContainedInInstancesOf: [SectionHeaderView.self])
// update the label(s) font property to the appearance font
headerView.titleLabel.font = a.font
headerView.infoLabel.font = a.font
// Use this view to calculate the optimal size based on the collection view's width
return headerView.systemLayoutSizeFitting(CGSize(width: collectionView.frame.width, height: UIView.layoutFittingCompressedSize.height), withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel)
}
Related Topics
Multiple Iboutlets in Same Line of Same Type in Swift
Create Navbar Programmatically with Button and Title Swift
How to Add .Plist File to All Targets in Xcode
Add Gps Metadata Dictionary to Image Taken with Avfoundation in Swift
How to Access My Swift Classes from My UI Tests
Blur Effect - Background Uitextfield
Core Data Entity Unique Constraint Does Not Work
How to Know a Device Is Available to Use Capturetextfromcamera API
It Is Possible to Know If a String Is Encoded in Base64
Peek/Pop Preview Ignores Cell Corner Radius in Collection View
Import Xctest into a Dynamic Framework
Adding Custom Game Logic to Scene Kit (Swift)
How to Bridge Nsnumber to Float in JSON Parsing
iOS Healthkit How to Save Heart Rate (Bpm) Values? Swift
Save Image with the Correct Orientation - Swift & Core Image
How to Set Uitextview's Height Dynamically in Uitableviewcell Based on String Size