Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

collectionView:viewForSupplementaryElementOfKind:atIndexPath: called only with UICollectionElementKindSectionHeader

Tags:

ios

ios6

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?

like image 901
Stelian Iancu Avatar asked Nov 02 '12 08:11

Stelian Iancu


3 Answers

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.

like image 72
Stelian Iancu Avatar answered Nov 12 '22 06:11

Stelian Iancu


(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.

like image 21
DawnSong Avatar answered Nov 12 '22 07:11

DawnSong


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;
}
like image 3
Kinphi Avatar answered Nov 12 '22 05:11

Kinphi