23

Having two headers in UICollectionView?

I've got a UICollectionView which uses the flow layout, which also has a header and footer:

---------   
| head  |
---------
| A | B |
---------
| C | D |
---------
| foot  |
---------

Occasionally, I'd like to have two headers, like so:

---------   
| head1 |
---------   
| head2 |
---------
| A | B |
---------
| C | D |
---------
| foot  |
---------

I'm stuck on how to achieve this. The flow layout only appears to allow one head and one foot. How can I add a second header?


edit: I have also implemented sticky headers - http://blog.radi.ws/post/32905838158/sticky-headers-for-uicollectionview-using - but I only want the first header to be sticky. This is why I can't just include everything in one header.

cannyboy
  • 24,180
  • 40
  • 146
  • 252

7 Answers7

35

You just need to use a simple trick.Show header and footer both for all sections.

In which section you do not want to show footer just pass its size zero as :--

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section
{
    if(section==0)
    {
        return CGSizeZero;
    }

    return CGSizeMake(320, 50);
}

Here I have used two sections like

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
    return 2;
}

And passed no of rows in only one sections that is the last one as

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    if (section==1) {
        return 20;
    }
    return 0;
}

And here is my output ---

enter image description here

Red View is header and Green One is footer.

Here u can get the entire Implementation File

Woodstock
  • 22,184
  • 15
  • 80
  • 118
Prince Kumar Sharma
  • 12,591
  • 4
  • 59
  • 90
  • This seems like a good direction to go. With this method could I use different layouts for the two headers? And allow me to let one header be 'sticky' and the other one to scroll normally. – cannyboy Aug 13 '13 at 14:57
  • yeah why not , just apply section condition on viewForSupplementaryElementOfKind and u can get header of different layouts for each section. – Prince Kumar Sharma Aug 14 '13 at 04:24
  • How can i set CustomFlowLayout only section 2? – ios developer Jul 30 '16 at 07:34
5

This content may help you to achieve what you want

create the class CollectionHeaderView and make it to derive from UICollectionReusableView and make container, And then after make 2 uiview and put it to this container

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

    if (kind == UICollectionElementKindSectionHeader) {
        CollectionHeaderView *headerView = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"HeaderView" forIndexPath:indexPath];

        headerView.firstContainer.titleLabel.text = @"MY Header View 1";//Here you can set title 

        headerView.secondContainer.titleLabel.text = @"MY Header View 2";//Here you can set title  
        UIImage *headerImage = [UIImage imageNamed:@"header_banner.png"];
        headerView.firstContainer.backgroundImage.image = headerImage;
       headerView.secondContainer.backgroundImage.image = headerImage;

        reusableview = headerView;
    }

    if (kind == UICollectionElementKindSectionFooter) {
        UICollectionReusableView *footerview = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionFooter withReuseIdentifier:@"FooterView" forIndexPath:indexPath];

        reusableview = footerview;
    }

    return reusableview;
}
SachinVsSachin
  • 6,401
  • 3
  • 33
  • 39
4

You should put both the header (1 and 2) in an other view and place that view as head 1. Thus create just on header in the collection view.

rckoenes
  • 69,092
  • 8
  • 134
  • 166
2

How are you adding one header? I suppose by specifying section headers? The recipe to have two headers would be to have two header subviews inside one header main view.

Nirav Bhatt
  • 6,940
  • 5
  • 45
  • 89
2

What you can do is use a UITableView with two sections and put the UICollectionView in the cell of the second section.

DrBug
  • 2,004
  • 2
  • 20
  • 21
2

Define your UICollectionViewCell which will be your Header view of kind UICollectionElementKindSectionHeader - In my case I have two headers - OfferHeaderCell and APRHeaderCell defined as below:

verticalCollectionView.register(UINib(nibName: "OfferHeaderCell", bundle: nil), forSupplementaryViewOfKind:UICollectionElementKindSectionHeader, withReuseIdentifier: "OfferHeaderCell")
verticalCollectionView.register(UINib(nibName: "APRHeaderCell", bundle: nil), forSupplementaryViewOfKind:UICollectionElementKindSectionHeader, withReuseIdentifier: "APRHeaderCell")

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 UICollectionViewDelegateFlowLayout function

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
    if(section==0) {
        return CGSize.zero
    } else if (section==1) {
        return CGSize(width:collectionView.frame.size.width, height:133)
    } else {
        return CGSize(width:collectionView.frame.size.width, height:100)
    }

}

Important to define the viewForSupplementaryElementOfKind for two different sections as below:

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

    var reusableview = UICollectionReusableView()
    if (kind == UICollectionElementKindSectionHeader) {
        let section = indexPath.section
        switch (section) {
        case 1:
            let  firstheader: OfferHeaderCell = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "OfferHeaderCell", for: indexPath) as! OfferHeaderCell
            reusableview = firstheader
        case 2:
            let  secondHeader: APRHeaderCell = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "APRHeaderCell", for: indexPath) as! APRHeaderCell
            reusableview = secondHeader
        default:
            return reusableview

        }
    }
    return reusableview
}

And lastly the Datasource,

func numberOfSections(in collectionView: UICollectionView) -> Int {
    return 3
}

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    if (section==2) {
        return 2
    }
    return 0
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = verticalCollectionView.dequeueReusableCell(withReuseIdentifier: "ReviseOfferCell", for: indexPath)
    cell.backgroundColor = UIColor.white
    return cell
}

Note: Don't forgot to add UICollectionFlowLayout as below:

// MARK: UICollectionViewDelegateFlowLayout

extension MakeAnOfferController: UICollectionViewDelegateFlowLayout {

        func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

            if indexPath.item == 0 {
                return CGSize(width: self.view.frame.size.width, height: 626.0)
            }
            return CGSize()
        }

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

            if(section==0) {
                return CGSize.zero
            } else if (section==1) {
                return CGSize(width:collectionView.frame.size.width, height:133)
            } else {
                return CGSize(width:collectionView.frame.size.width, height:100)
            }
        }
    }
Tarun
  • 329
  • 1
  • 4
  • 16
0

Got this working by creating two views in a reusable header, implement sticky header only if the section is the second section. Also, I adjust the numberOfSection to 2. Got the headers switching by hiding and showing the views in viewForSupplementaryElementOfKind.

John Paul Manoza
  • 1,735
  • 25
  • 22