Reputation: 18745
I would like to add a section header to a UICollectionView
which contains two lables to show a title and a info description. Both labels can show multi line texts and the header size should be automatically adopted to the label content.
TL;DR
Auto layout seems to fails when the label font is changed the appearance()
proxy. Is there solution to solve this?
Complete description
This is how I set up the header view in IB:
As one can see, IB reports a problem. This is a "Content Priority Ambiguity" which can easily be solved by setting the Vertical Content Hugging Priority of InfoLabel to 250.
The header view is than added in the ViewController and referenceSizeForHeaderInSection
is used to setup the size:
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
if kind == UICollectionView.elementKindSectionHeader {
let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "header", for: indexPath) as? SectionHeaderView ?? SectionHeaderView()
headerView.titleLabel.text = "Title Label with long text. More text..."
headerView.infoLabel.text = "Info Label with long text. More text...."
return headerView
}
return UICollectionReusableView()
}
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)
// 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)
}
So far everything looks good. However, when the label font is changed using the appearance()
proxy, it fails:
// called in application:didFinishLaunchingWithOptions
UILabel.appearance(whenContainedInInstancesOf: [SectionHeaderView.self]).font = UIFont.systemFont(ofSize: 10)
The left screenshot shows the layout without the changing the font size, the right screenshot the layout after using UILabel.appearance
:
It seems that the font size is updated after the header size was calculated and that now re-calculation is triggered.
How can this be solved?
If both labels have the same hugging priority (error in IB is simply ignored), the problem is almost the same. However, without explicit information auto layout simply decides that the title label is stretched instead of the info label.
Upvotes: 2
Views: 1268
Reputation: 77638
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)
}
Upvotes: 1