I have a collection view and I would like each section to have both a header and a footer. I'm using the default flow layout.
I have my own subclasses of UICollectionReusableView
and I register each for both the header and the footer in the viewDidLoad
method of my view controller.
I've implemented the method collectionView:viewForSupplementaryElementOfKind:atIndexPath:
but, for each section, it is only called with kind
being UICollectionElementKindSectionHeader
. Therefore my footer isn't even created.
Any ideas why this happens?
It seems that I have to set the footerReferenceSize
for the collection view layout. Weird that I didn't have to do that with the header.
(Using Swift 3.1, Xcode 8.3.3)
First, register header's class or nib
collectionView.register(ShortVideoListHeader.self, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "header")
Second, set headerReferenceSize
; alternatively, you can return headerReferenceSize
in collectionView's delegate
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
(collectionView.collectionViewLayout as? UICollectionViewFlowLayout)?.headerReferenceSize = CGSize(width: view.bounds.width, height: 156)
}
Third, write your own header class, such as,
class ShortVideoListHeader: UICollectionReusableView {
let titleLabel = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(titleLabel)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
titleLabel.sizeToFit()
titleLabel.frame.origin = CGPoint(x: 15, y: 64 + (frame.height - 64 - titleLabel.frame.height) / 2) // navigationBar's height is 64
}
}
Fourth, return your header instance in collectionView's dataSource methods,
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
switch kind {
case UICollectionElementKindSectionHeader:
let header = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "header", for: indexPath) as! ShortVideoListHeader
header.titleLabel.text = title
header.setNeedsLayout()
return header
default:
return UICollectionReusableView()
}
}
By the way, Apple's Document answers how to hide section header.
This method must always return a valid view object. If you do not want a supplementary view in a particular case, your layout object should not create the attributes for that view. Alternatively, you can hide views by setting the hidden property of the corresponding attributes to YES or set the alpha property of the attributes to 0. To hide header and footer views in a flow layout, you can also set the width and height of those views to 0.
I found some code maybe can help you
- ( UICollectionReusableView * ) collectionView : ( UICollectionView * ) collectionView viewForSupplementaryElementOfKind : ( NSString * ) kind atIndexPath : ( NSIndexPath * ) indexPath
{
UICollectionReusableView * reusableview = nil ;
if ( kind == UICollectionElementKindSectionHeader )
{
RecipeCollectionHeaderView * headerView = [ collectionView dequeueReusableSupplementaryViewOfKind : UICollectionElementKindSectionHeader withReuseIdentifier : @ "HeaderView" forIndexPath : indexPath ] ;
NSString * title = [ [ NSString alloc ] initWithFormat : @ "Recipe Group #%i" , indexPath.section + 1 ] ;
headerView.title.text = title;
UIImage * headerImage = [ UIImage imageNamed : @ "header_banner.png" ] ;
headerView.backgroundImage.image = headerImage;
reusableview = headerView;
}
if ( kind == UICollectionElementKindSectionFooter )
{
UICollectionReusableView * footerview = [ collectionView dequeueReusableSupplementaryViewOfKind : UICollectionElementKindSectionFooter withReuseIdentifier : @ "FooterView" forIndexPath : indexPath ] ;
reusableview = footerview;
}
return reusableview;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With