24

I am trying to add header to collectionView using custom xib file. I created the xib file with class implementing UICollectionReusableView. In collectionViewController I registered the xib file like this:

self.collectionView.register(UINib(nibName: HCollectionReusableView.nibName, bundle: nil), forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: HCollectionReusableView.reuseIdentifier)

and after that in viewForSupplementaryElementOfKind I did

let header = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: HCollectionReusableView.reuseIdentifier, for: indexPath) as! HCollectionReusableView

and for sizing

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
    return CGSize(width: 100, height: 50)
}

I am getting error: Could not load NIB in bundle. any missing code ?

HCollectionReusableView class:

class HCollectionReusableView: UICollectionReusableView {

static var nibName : String
    {
    get { return "headerNIB"}
}

static var reuseIdentifier: String
    {
    get { return "headerCell"}
}



override func awakeFromNib() {
    super.awakeFromNib()
    // Initialization code
}

}

Jack
  • 13,571
  • 6
  • 76
  • 98
Khaled Alhayek
  • 553
  • 2
  • 6
  • 21

3 Answers3

48

You need to call viewForSupplementaryElementOfKind like this:

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

    switch kind {
    case UICollectionElementKindSectionHeader:
           let reusableview = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "HCollectionReusableView", for: indexPath) as! HCollectionReusableView

            reusableview.frame = CGRect(0 , 0, self.view.frame.width, headerHight)
         //do other header related calls or settups
            return reusableview


    default:  fatalError("Unexpected element kind")
    }
}

This way you can initialise and show the header

Another way of setting the UICollectionViewHeader frame is by extending UICollectionViewDelegateFlowLayout like this:

extension UIViewController: UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
        return CGSize(width: collectionView.frame.width, height: 100) //add your height here
    }
}

This removes the need to call :

reusableview.frame = CGRect(0 , 0, self.view.frame.width, headerHight)

in the above mentioned

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

Remember to register the HeaderView after you initialise your UICollectionView by calling:

collectionView.register(UINib(nibName: HCollectionReusableView.nibName, bundle: nil), forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "HCollectionReusableView")

Swift 4.1 Update

UICollectionElementKindSectionHeader has been renamed to UICollectionView.elementKindSectionHeader

anho
  • 1,705
  • 2
  • 20
  • 38
  • I think I have to change the registration of xib to class, here the error now: could not dequeue a view of kind: UICollectionElementKindSectionHeader with identifier HCollectionReusableView - must register a nib or a class for the identifier or connect a prototype cell in a storyboard (null) – Khaled Alhayek Feb 13 '17 at 09:41
  • 2
    you get the error since the identifiers when you deque the item and when you register don't match ... try to hardcode the identifier like this when you register the cell : `self.collectionView.register(UINib(nibName: HCollectionReusableView.nibName, bundle: nil), forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "HCollectionReusableView") ` this should work ... then you can just adjust the code to fit your needs – anho Feb 13 '17 at 09:42
  • you will register class only if you don't use a Xib or Storyboard at all – anho Feb 13 '17 at 09:44
  • 1
    great, save my day – Mr.Javed Multani Jun 05 '20 at 11:35
  • You still need to register your class, not the nib, otherwise it won't type cast. yourCollectionView.register(YourClassName.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "YourHardCodeIdentifierString") – M.Yessir Feb 05 '21 at 15:38
3
final class MyCollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout {

override func viewDidLoad() {
    super.viewDidLoad()

    self.collectionView!.register(UICollectionReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: MyCollectionReusableView.reuseIdentifierHeader)
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
    return CGSize(width: collectionView.frame.width, height: 40)
}

override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
    let header = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: MyCollectionReusableView.reuseIdentifierHeader, for: indexPath) as! MyCollectionReusableView
    header.setupView()
    
    return header
}
}

final class MyCollectionReusableView: UICollectionReusableView {

   static let reuseIdentifierHeader = "MyId"

   func setupView() {
      //Code...
   }

}
Loma
  • 31
  • 1
0

Did you set File' Owner setting in your xib File? Change file owner to your View controller hosting collectionView.

enter image description here

Isuru
  • 30,617
  • 60
  • 187
  • 303