Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Header of section in UICollectionView with custom UICollectionViewLayout

I'm working on UICollectionView with custom layout. In two days I cannot understand how add header to UICollectionView. I've got very simple view controller (created in storyboard with custom layout):

class ACollectionViewController: UICollectionViewController {

    enum Identifiers: String {
        case CellIdentifier = "Cell"
        case HeaderIdentifier = "Header"
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        self.collectionView.registerClass(UICollectionReusableView.self, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: Identifiers.HeaderIdentifier.rawValue)
    }

    // MARK: UICollectionViewDataSource
    override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
        return 1
    }

    override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 10
    }

    override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier(Identifiers.CellIdentifier.rawValue, forIndexPath: indexPath) as Cell
        cell.backgroundColor = UIColor.redColor()
        return cell
    }

    override func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {
        println("ip = \(indexPath.item)")
        var supplementaryView = collectionView.dequeueReusableCellWithReuseIdentifier(Identifiers.HeaderIdentifier.rawValue, forIndexPath: indexPath) as UICollectionReusableView
        supplementaryView.backgroundColor = UIColor.blueColor()
        return supplementaryView
    }

And here is my custom layout:

class ALayout: UICollectionViewLayout {

    override func prepareLayout() {
        super.prepareLayout()
    }

    override func collectionViewContentSize() -> CGSize {
        return self.collectionView!.bounds.size
    }

    let itemWidth = 40
    override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes! {
        var attributes = UICollectionViewLayoutAttributes(forCellWithIndexPath: indexPath)

        let x: Float = Float(10) * Float(indexPath.item)
        let y: Float = Float(indexPath.item * itemWidth)
        attributes.frame = CGRect(x: CGFloat(x), y: CGFloat(y), width: CGFloat(itemWidth), height: CGFloat(itemWidth))

        return attributes
    }

    override func layoutAttributesForElementsInRect(rect: CGRect) -> [AnyObject]? {
        var attributes = [UICollectionViewLayoutAttributes]()

        let sectionsCount = self.collectionView!.dataSource!.numberOfSectionsInCollectionView!(self.collectionView!)
        for section in 0..<sectionsCount {
            /// add header
            attributes.append(self.layoutAttributesForSupplementaryViewOfKind(UICollectionElementKindSectionHeader, atIndexPath: NSIndexPath(forItem: 0, inSection: section)))

            let itemsCount = self.collectionView!.numberOfItemsInSection(section)
            for item in 0..<itemsCount {
                let indexPath = NSIndexPath(forItem: item, inSection: section)
                attributes.append(self.layoutAttributesForItemAtIndexPath(indexPath))
            }
        }

        return attributes
    }

    override func layoutAttributesForSupplementaryViewOfKind(elementKind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes! {
        var attributes = UICollectionViewLayoutAttributes(forSupplementaryViewOfKind: elementKind, withIndexPath: indexPath)
        attributes.frame = CGRect(x: 0, y: 0, width: 320, height: 50)

        return attributes
    }
}

The problem is I don't understand how to add section header correctly, what indexPath of this header should be. There is the comment in code where header is added. App is crashing at start with error

2014-10-21 13:06:22.793 UICollectionViewLayout-Demo[3805:95924] *** Terminating app due to 
uncaught exception 'NSInternalInconsistencyException', reason: 'could not dequeue a view of 
kind: UICollectionElementKindCell with identifier Header - must register a nib or a class for 
the identifier or connect a prototype cell in a storyboard'

And I understand, this is because cell has the same indexPath as section header I'm trying to add, but what should be indexPath of the header? Or maybe I'm doing it totally wrong?

Thank you in advance.

like image 858
Tomasz Szulc Avatar asked Oct 21 '14 11:10

Tomasz Szulc


People also ask

How do I add a section header in collection view?

There are no section headers in the UICollectionView. So for your first task, you'll add a new section header using the search text as the section title. To display this section header, you'll use UICollectionReusableView .

What is Uicollectionviewlayout?

An abstract base class for generating layout information for a collection view.

How do I register a collection view cell?

Use a cell registration to register cells with your collection view and configure each cell for display. You create a cell registration with your cell type and data item type as the registration's generic parameters, passing in a registration handler to configure the cell.


3 Answers

Your crash is arising because you are using

var supplementaryView = collectionView.dequeueReusableCellWithReuseIdentifier(Identifiers.HeaderIdentifier.rawValue, forIndexPath: indexPath) as UICollectionReusableView

to try to dequeue the header and footer (in collectionView:viewForSupplementaryElementOfKind). Changing it to

var supplementaryView = collectionView.dequeueReusableSupplementaryViewOfKind(kind, withReuseIdentifier:Identifiers.HeaderIdentifier.rawValue, forIndexPath: indexPath) as UICollectionReusableView

should get rid of the crash, so you can investigate the indexPath. But on my test, indexPath is always just "0".

like image 163
pbasdf Avatar answered Oct 11 '22 10:10

pbasdf


You are using wrong function for header. As per apple documentation use the following for header and footer:

func dequeueReusableSupplementaryViewOfKind(_ elementKind: String,
                        withReuseIdentifier identifier: String,
                               forIndexPath indexPath: NSIndexPath!) -> AnyObject

instead of:

 var supplementaryView = collectionView.dequeueReusableCellWithReuseIdentifier(Identifiers.HeaderIdentifier.rawValue, forIndexPath: indexPath) as UICollectionReusableView
like image 37
Aniket Bochare Avatar answered Oct 11 '22 10:10

Aniket Bochare


Instead of

var supplementaryView = collectionView.dequeueReusableCellWithReuseIdentifier(Identifiers.HeaderIdentifier.rawValue, forIndexPath: indexPath) as UICollectionReusableView       

Should be

let supplementaryView = collectionView.dequeueReusableSupplementaryViewOfKind(kind withReuseIdentifier:Identifiers.HeaderIdentifier.rawValue, forIndexPath:indexPath)
like image 21
Michal Zaborowski Avatar answered Oct 11 '22 11:10

Michal Zaborowski