I've read a bunch of posts on adding header to UICollectionView. In an iOS 7+ app in Swift, I'm trying to add a header with a UILabel in it whose height should adjust based on the height of UILabel. The UILabel has lines = 0.
I've set up the header in IB with AutoLayout
The ViewController implements UICollectionViewDelegate, UICollectionViewDataSource
. I didn't set up a custom class for the header but am using these two functions:
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
//description is a String variable defined in the class
let size:CGSize = (description as NSString).boundingRectWithSize(CGSizeMake(CGRectGetWidth(collectionView.bounds) - 20.0, 180.0), options: NSStringDrawingOptions.UsesLineFragmentOrigin, attributes: [NSFontAttributeName: UIFont(name: "Helvetica Neue", size: 16.0)], context: nil).size
return CGSizeMake(CGRectGetWidth(collectionView.bounds), ceil(size.height))
}
func collectionView(collectionView: UICollectionView!, viewForSupplementaryElementOfKind kind: String!, atIndexPath indexPath: NSIndexPath!) -> UICollectionReusableView! {
var reusableview:UICollectionReusableView = UICollectionReusableView()
if (kind == UICollectionElementKindSectionHeader) {
//listCollectionView is an @IBOutlet UICollectionView defined at class level, using collectionView crashes
reusableview = listCollectionView.dequeueReusableSupplementaryViewOfKind(UICollectionElementKindSectionHeader, withReuseIdentifier: "ListHeader", forIndexPath: indexPath) as UICollectionReusableView
let label = reusableview.viewWithTag(200) as UILabel //the UILabel within the header is tagged with 200
label.text = description //description is a String variable defined in the class
}
}
return reusableview
}
The displaying of the text seems to be working but the height calculation doesn't seem to be working (see screenshot below). Also, I don't think I can access the UILabel via the collectionView...referenceSizeForHeaderInSection
function either. Any suggestions on how to calculate CGSize correctly?
The idea is to have a template header instance in memory to calculate the desired height before creating the result header view. You should move your section header view to a separate .nib file, setup all autolayout constraints and instantiate the template in your viewDidLoad method like this:
Then you will be able to calculate the header size (height in my example) in your flow layout delegate method:
And then create and return your regular header view as always, since you have already calculated its size in flow layout delegate method:
Forgot to say about one important moment - your header view should have an autolayout constraint on its contentView to fit the collection view width (plus or minus the desired margins).
This is how I did it:
The basic idea is to create an identical
UILabel
to the one that will be shown in the section header. That label will be used to set the desired size for the header in thereferenceSizeForHeaderInSection
method.I have a label outlet called
label
in myUICollectionReusableView
subclass (MyHeaderCollectionReusableView
), which I use for my section header view by assigning it in the storyboard (setting "MyHeader" as Reuse Identifier for the section view). That mentioned label has the horizontal and vertical space constraints to the section header borders in order to autolayout correctly.I had luck using Vladimir's method, but I had to set the frame of the template view to have equal width to my collection view.
Additionally, my view has several resizable components, and having a template view seems robust enough to deal with any changes. Still feels like there should be an easier way.
You have to implement the
UICollectionViewDelegate
methodreferenceSizeForHeaderInSection
.There you have to calculate the height without using the label by calling
boundingRectWithSize:options:context:
on the string with the appropriate attributes.Like the questioner, I had a UICollectionView that contained a header with a single label, whose height I wanted to vary. I created an extension to
UILabel
to measure the height of a multiline label with a known width:Note: the above is in Swift 3 syntax.
Then I implement the header size method of
UICollectionViewDelegateFlowLayout
as:The work of calculating the header size is delegated to the above
UILabel
extension. The+16
is a experimentally derived fixed offset (8 + 8) that is based on margins and could be obtained programmatically.All that's needed in the header callback is just to set the text: