cannyboy
cannyboy

Reputation: 24426

UICollectionView with two headers

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.

Upvotes: 23

Views: 16571

Answers (7)

Tarun
Tarun

Reputation: 349

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)
            }
        }
    }

Upvotes: 2

John Paul Manoza
John Paul Manoza

Reputation: 1735

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.

Upvotes: 0

Prince Kumar Sharma
Prince Kumar Sharma

Reputation: 12641

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

Upvotes: 35

DrBug
DrBug

Reputation: 2024

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

Upvotes: 2

SachinVsSachin
SachinVsSachin

Reputation: 6427

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;
}

Upvotes: 5

Nirav Bhatt
Nirav Bhatt

Reputation: 6969

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.

Upvotes: 2

rckoenes
rckoenes

Reputation: 69469

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.

Upvotes: 4

Related Questions