add custom header to collection view swift

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

}

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