Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UICollectionView Multiple Sections and Headers

Tags:

I am struggling trying to do multiple sections in my collection view with a header for each section. I don't know Obj-C and I've found a good amount of tutorials for it, but haven't been able to figure out how to convert it into Swift.

All my data is static so all I need is some sort of array or dictionary that I can use to create the multiple sections. I already have a collection view with 1 section working, so if you have any insight or code for multiple sections that'd be helpful.

I know how to set multiple sections using

func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int   {
    return sectionData.count
}

I think the main thing I need help with is implementing this func

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { }

and setting up the data!

UICollectionView and UITableView are almost exactly the same, so if you know how to do multiple sections in a UITableView in Swift, your help is also appreciated

like image 761
user3399235 Avatar asked Jan 23 '15 03:01

user3399235


People also ask

How do you create a collection view with multiple sections?

You need to implement a custom UICollectionViewLayout. With a horizontal flow layout it's going to fill from top to bottom first, then move to the right. Since you have two rows, as specified in sizeForItemAt, section 0 will fill from top to bottom, then right to left, and so will section 1.

How do I have more than one type of cell in a collection view?

Just like you would for a single cell. Dragging multiple cells into your collection view in a Storyboard automatically registers these cells on your collection view.


2 Answers

The cellForItemAtIndexPath function handles populating each section with cells, it does not handle sections or supplementaryViews, and therefore is not the main thing you need help with when it comes to creating section headers.

the method you need to implement is viewForSupplementaryElementOfKind. Its signature is:

func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {}

Assuming that your collectionView is working correctly for 1 section (you have properly filled out the body of your cellForItemAtIndexPath and your sectionData array properly reflects the number of sections you want to display), you should be able to implement section headers using the following pointers:

Along with cells, UICollectionView also supports "supplementary" view objects, typically used for headers or footers. These Supplementary Views act very similarly to UICollectionViewCell objects. In the same way that cellForItemAtIndexPath handles cells, The viewForSupplementaryElementOfKind function handles supplementary views.

To implement it, you will need to first prepare your ViewController to do so. First edit your layout object to reflect an appropriate header size, that each header will adhere to:

 let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
 layout.headerReferenceSize = CGSize(width: self.view.frame.size.width, height: 30)

NOTE: I am using a UICollectionViewFlowLayout

Next, if you haven't already done so, create a SectionHeader class that defines each section header object, so you can then register that class with your collectionView object like so:

  collectionView!.registerClass(SectionHeaderView.self, forSupplementaryViewOfKind:UICollectionElementKindSectionHeader, withReuseIdentifier: "SectionHeaderView");

Here, the first and third argument passed in are the same as a UICollectionViewCell Class registration, the first argument in this method is the reference to the section header class you created. The third is the reuse identifier for the Supplementary View.

The second argument is specific to Supplementary Views, this sets the kind of the SupplementaryView, which in this case is a header, the constant string provided by the UICollectionViewFlowLayout class UICollectionElementKindSectionHeader is used for it. If you noticed the parameters on the viewForSupplementaryElementOfKind, this kind is later passed in as the kind: String parameter.

Fill in the body of your viewForSupplementaryElementOfKind the same way you would for a cellForItemAtIndexPath function-- Using the dequeueReusableSupplementaryViewOfKind method to create a SectionHeader object, then set any attributes as necessary (labels, colors, etc.) and finally return the header object.

Hope this helps!!

Reference points:

https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UICollectionViewDataSource_protocol/index.html#//apple_ref/occ/intfm/UICollectionViewDataSource/

https://developer.apple.com/library/ios/documentation/UIKit/Reference/UICollectionViewFlowLayout_class/index.html#//apple_ref/c/data/UICollectionElementKindSectionHeade

like image 152
mkncode Avatar answered Sep 19 '22 21:09

mkncode


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)
            }
        }
    }
like image 41
Tarun Avatar answered Sep 17 '22 21:09

Tarun