CollectionViewのHeaderを作る上でカスタマイズできる点についてまとめました。Swift4.0で書いています。
一応GitHubにコードを置いています。
https://github.com/akatsuki174/CollectionViewHeaderCustomizeSample
前提:Headerを表示するまで
UICollectionViewControllerを使って作る場合の一例がこちらになります。
storyboard
 
cellはUICollectionViewControllerを用意した時にデフォルトでついていたものを使っています。headerはUICollectionReusableViewをObject Libraryから引っ張ってきて置いています。このheaderにはSectionHeaderというidentifierを振っています。
headerクラス
ラベル置いているだけなのでこれだけしか書いていません。
import UIKit
final class SectionHeader: UICollectionReusableView {
    @IBOutlet weak var sectionLabel: UILabel!
}
ViewControllerクラス
import UIKit
final class ViewController: UICollectionViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
    }
}
// MARK: datasorce, delegate
extension ViewController {
    override func collectionView(_ collectionView: UICollectionView,
                        cellForItemAt indexPath: IndexPath) -> UICollectionViewCell{
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
        return cell
    }
    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 10
    }
    override func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 3
    }
    override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
        guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "SectionHeader", for: indexPath) as? SectionHeader else {
            fatalError("Could not find proper header")
        }
        if kind == UICollectionElementKindSectionHeader {
            header.sectionLabel.text = "section \(indexPath.section)"
            return header
        }
        return UICollectionReusableView()
    }
}
この時点でのUI
 
高さのカスタマイズ
全てのセクションヘッダを同じ高さにするのであればviewDidLoadを以下のようにすれば設定することができます。
override func viewDidLoad() {
    super.viewDidLoad()
    guard let fl = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout else { return }
    fl.headerReferenceSize = CGSize(width: self.view.bounds.width, height: 30)
}
 
セクションによってヘッダの高さを変えたい場合はUICollectionViewDelegateFlowLayoutプロトコルを適用させた上で次のメソッドを追加すると高さを調節することができます。
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
    if section == 1 {
        return CGSize(width: self.view.bounds.width, height: 50)
    } else {
        return CGSize(width: self.view.bounds.width, height: 30)
    }
}
ヘッダを表示しないセクションを作る
セクションによってセクションを出したい時、出したくない時があると思います。その時はさっき使ったメソッドを使って以下のように(0,0)を返します。
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
    if section == 1 {
        return CGSize.zero
    } else {
        return CGSize(width: self.view.bounds.width, height: 30)
    }
}
 
ヘッダを画面上部に固定する
上にスクロールさせた時にセクションヘッダを画面上部に固定する場合は以下のようにviewDidLoadにコードを追加します(これを書かずに今まで通りのコードを書いていれば画面上部に固定されません)。
override func viewDidLoad() {
    super.viewDidLoad()
    guard let fl = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout else { return }
    fl.sectionHeadersPinToVisibleBounds = true
}
 
xibで作ったヘッダを設定する
ヘッダはこんなふうに作りました。
 
final class XibHeader: UICollectionReusableView {
    @IBOutlet weak var sectionLabel: UILabel!
    static let identifier: String = "XibHeader"
    static func nib() -> UINib {
        return UINib(nibName: XibHeader.identifier, bundle: nil)
    }
}
上記ヘッダをセットするために、ViewController.swiftに以下のコードを足しています。
override func viewDidLoad() {
    super.viewDidLoad()
    // 今まで書いた他の要素は省略
    collectionView?.register(XibHeader.nib(), forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: XibHeader.identifier)
}
override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
    if kind == UICollectionElementKindSectionHeader {
        if indexPath.section == 0 || indexPath.section == 4 {
            guard let xibHeader = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: XibHeader.identifier, for: indexPath) as? XibHeader else {
                fatalError("Could not find proper header")
            }
            xibHeader.sectionLabel.text = "section \(indexPath.section)"
            return xibHeader
        } else {
            guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "SectionHeader", for: indexPath) as? SectionHeader else {
                fatalError("Could not find proper header")
            }
            header.sectionLabel.text = "section \(indexPath.section)"
            return header
        }
    }
    return UICollectionReusableView()
}
