Allow A Header View for Only Certain Sections Using an iOS UICollectionView

I noticed that the accepted answer broke when you used AutoLayout in the XIB for the reusable header.

It was especially broken if you spaced the content from the edges or gave the items inside the header view a static, immutable size. Setting the header size to CGSizeZero cluttered my debugger console with dozens of warnings from Interface Builder saying that they'd break all the constraints to meet the requirement set in the delegate method.

While that in itself is not technically a disaster, it's still dirty. And in the age of Swift and AutoLayout there has to be a cleaner solution. Also you never want to ship that sort of thing to a client when you're at work.

To fix this, instead of just invoking referenceSizeForHeaderInSection: and returning CGSizeZero I created another subclass of UICollectionReusableView with XIB and set the height of the view inside it to 0.

Then later, I dequeue that variant outside my switch statement contained within the viewForSupplementaryElementOfKind method. This satisfies both Interface Builder and visual requirements! 🎉

Beats having hundreds of unsatisfiable constraint warnings printed in the Console while you're debugging, anyhow.


I had a case with one UICollectionViewController controlling two UICollectionViews (referenced later as collection view 1 and 2) and I wanted headers to the first and no headers (or footers) to the second.

What's missing from @mwright's answer is that when you return CGSizeZero for collection view 2 as follows:

func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
    if collectionView == self.collectionView2 {
        return CGSizeZero
    }
    return < something else >
}

... means that the collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView does not get called at all for collection view 2.

Meaning that you don't need to worry about returning a "wrong" header for the second collection view in vain.


If you return a value of size (0, 0), no header will be added.

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {

 switch section {
 case 0:
   return CGSize(width: collectionView.bounds.width, height: 70)
 case 1:
   return CGSize(width: 0, height: 0) // NO HEADER WILL BE ADDED
 case 2:
   return CGSize(width: collectionView.bounds.width, height: 70)
 case 3:
   return CGSize(width: 0, height: 0) // NO HEADER WILL BE ADDED
 default:
   return CGSize(width: collectionView.bounds.width, height: 70)
 }

}
 

Go ahead and return a header for each section and then set the size of the section header to have a size of zero in this UICollectionViewDelegate function.

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section
{
    if (section == 0) {
        return CGSizeZero;
    }else {
        return CGSizeMake(self.collectionView.bounds.size.width, desiredHeight);
    }
}