81

I want my sections in the UICollectionView to have a header with an image.

I have followed these steps:

  • at the storyboard, assigned a header as an accessory for my UICollectionView
  • gave it an identifier
  • created a subclass of UICollectionReusableView for it
  • assigned the custom class to the class at the storyboard.
  • put an ImageView at the header accessory
  • made an outlet for the ImageView at the custom class in the .h file
  • Implemented the following at viewDidLoad:

 

[self.collectionView registerClass:[ScheduleHeaderView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:headerIdentifier];

-(UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
    UICollectionReusableView *reusableview = nil;

    if (kind == UICollectionElementKindSectionHeader)
    {
        ScheduleHeaderView *headerView = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:headerIdentifier forIndexPath:indexPath];

        headerView.headerImageView.image = [UIImage imageNamed:@"blah.png"];

        reusableview = headerView;
    }

    return reusableview;
}

I know that the datasource and delegate methods are working because I can see all the cells and its sections. However, I don't have my headers. I put in a breakpoint at the method above and it's never being called.

What am i doing wrong?

Spooky
  • 2,848
  • 8
  • 24
  • 39
Eden V.
  • 833
  • 1
  • 6
  • 10
  • 3
    Note that you compare the string pointers `kind == UICollectionElementKindSectionHeader` instead of the strings contents, you probably want to use `[kind isEqualToString: UICollectionElementKindSectionHeader]` instead. – Mac_Cain13 Apr 03 '14 at 11:31

12 Answers12

207

It seems that you have to give your header a non-zero size or collectionView:viewForSupplementaryElementOfKind:atIndexPath isn't called. So either set the headerReferenceSize property of the flow layout like so:

flowLayout.headerReferenceSize = CGSizeMake(self.collectionView.frame.size.width, 100.f);

Swift 5+

flowLayout.headerReferenceSize = CGSize(CGSize(width: self.collectionView.frame.size.width, height: 100))

or, implement collectionView:layout:referenceSizeForHeaderInSection if you want to vary the size by section.

Naveed Ahmad
  • 6,199
  • 2
  • 54
  • 80
rdelmar
  • 102,832
  • 11
  • 203
  • 218
33

You answered the question, but in words I understand better:

To make the method being called, add these methods:

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section {
    return CGSizeMake(60.0f, 30.0f);
}

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section {
    return CGSizeMake(60.0f, 30.0f);
}

...and go from there.

Natan R.
  • 5,003
  • 1
  • 29
  • 46
Pear Johan
  • 389
  • 5
  • 2
32

Swift 4 Xcode 9.1 Beta 2

Add @objc

@objc func collectionView(_ collectionView: UICollectionView, layout  collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize{
    let size = CGSize(width: 400, height: 50)
    return size
}

Credits: https://medium.com/@zonble/your-delegation-methods-might-not-be-called-in-swift-3-c6065ed7b4cd

cwilliamsz
  • 686
  • 9
  • 20
  • Thank you! Saved my butt. Btw, I noticed this function doesn't show up in the auto-complete list for collectionViewDelegate. I don't know why, so weird. Also weird that we need the @objc tag in front of it. – C0D3 Dec 13 '17 at 19:16
  • 3
    Only adding @objc to the function in Swift 4 worked for me. Ridiculous. – Louis Cremen Jan 25 '18 at 06:36
  • 7
    Conforming to UICollectionViewDelegateFlowLayout instead of UICollectionViewDelegate also fixes this without @objc if you're using UICollectionViewFlowLayout. – nemissm Apr 10 '18 at 12:16
  • it is a lot better if you use the collectionView bounds to set the with of your section header/footer: let size = CGSize(collectionView.bounds.width, height: 50) – omaestra Mar 20 '19 at 15:17
18

Did you add UICollectionViewDelegateFlowLayout in current interface? This worked for me.

@interface MyViewController () <UICollectionViewDelegateFlowLayout>

Swift:

class MyViewController: UICollectionViewDelegateFlowLayout {

Kerberos
  • 3,656
  • 3
  • 31
  • 51
CheshireKat
  • 424
  • 3
  • 9
9

Swift 3.0 (xcode 8.2.1)

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
    return CGSize(width:collectionView.frame.size.width, height:30.0)
}
Kerberos
  • 3,656
  • 3
  • 31
  • 51
Zeeshan
  • 3,886
  • 25
  • 32
5

There is the swift version :

func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
    let size = CGSize(width: 400, height: 50)
    return size
}

func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
    let size = CGSize(width: 400, height: 50)
    return size
}

XCode (version 7.3.1 ) does not propose these methods in autocomplete !

If you just want a header, just keep the header method.

Kevin ABRIOUX
  • 12,949
  • 7
  • 80
  • 78
3
    @objc (collectionView:viewForSupplementaryElementOfKind:atIndexPath:)
    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { }

The above code worked for me

Bibin Jaimon
  • 338
  • 1
  • 3
  • 14
2

My problem was that in swift 3, the name of the viewForSupplementaryElementOfKind function has changed slightly from any posts that were on stack overflow. Hence, it was never getting called. Make sure that, if you're in swift 3, you're delegate function matches:

func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {}
Daniel Jones
  • 882
  • 7
  • 15
  • Mine too. For me there was a missing "_", "atIndexPath" instead of "at" and "NSIndexPath" instead of "IndexPath"; as I have copied the method from somewhere and IDE didn't give me an error. – Ashkan Sarlak Jan 01 '18 at 12:59
1

I have the same issue. I have added referenceSizeForFooterInSection but then also viewForSupplementaryElementOfKind not getting called. I have added this code in viewDidLoad() and it worked for me:

if let flowLayout = self.collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
    flowLayout.sectionFootersPinToVisibleBounds = true   
}
Kerberos
  • 3,656
  • 3
  • 31
  • 51
Twinkle
  • 21
  • 2
1

Swift 5 Smooth and easy way

only these two line are missing in your code and you will done

use these line any where

let layout = self.colv.collectionViewLayout as! UICollectionViewFlowLayout
layout.headerReferenceSize = CGSize(width: self.colv.frame.size.width, height: 100)

I am using these lines in

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    let CellCount = CGFloat(3.0)
    let width = CGFloat(collectionView.frame.size.width/CellCount)
    //let height = CGFloat(collectionView.frame.size.height)

    
    let layout = self.colv.collectionViewLayout as! UICollectionViewFlowLayout

    layout.headerReferenceSize = CGSize(width: self.colv.frame.size.width, height: 100)
    
    return CGSize(width: width, height: width+10)
}
Shakeel Ahmed
  • 3,089
  • 24
  • 24
0

For me, in Swift 4.2, func collectionView(collectionView:kind:indexPath:) -> UICollectionReusableView was never being called. This was for a collection view in a UIViewController. I noticed the existing collection view functions were qualified with @objc, and that the UICollectionView had not adopted the UICollectionViewDataSource and UICollectionViewDelegate protocols. As soon as I adopted the protocols, I got errors that the collection view functions did not match the protocol. I corrected the function syntax, removed the qualifiers, and the section headers started working.

trishcode
  • 2,007
  • 15
  • 21
0

If you've got a generic subclass then you must take care to specify ObjC delegate method name if it's different then Swift name (https://bugs.swift.org/browse/SR-2817).

@objc (collectionView:viewForSupplementaryElementOfKind:atIndexPath:)
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
...