72
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

posted at

UICollectionViewのHeaderカスタマイズいろいろ

CollectionViewのHeaderを作る上でカスタマイズできる点についてまとめました。Swift4.0で書いています。

一応GitHubにコードを置いています。
https://github.com/akatsuki174/CollectionViewHeaderCustomizeSample

前提:Headerを表示するまで

UICollectionViewControllerを使って作る場合の一例がこちらになります。

storyboard

スクリーンショット 2017-11-12 0.03.14.png

cellはUICollectionViewControllerを用意した時にデフォルトでついていたものを使っています。headerはUICollectionReusableViewをObject Libraryから引っ張ってきて置いています。このheaderにはSectionHeaderというidentifierを振っています。

headerクラス

ラベル置いているだけなのでこれだけしか書いていません。

SectionHeader.swift
import UIKit

final class SectionHeader: UICollectionReusableView {
    @IBOutlet weak var sectionLabel: UILabel!
}

ViewControllerクラス

ViewController.swift
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

スクリーンショット 2017-11-12 0.14.13.png

高さのカスタマイズ

全てのセクションヘッダを同じ高さにするのであればviewDidLoadを以下のようにすれば設定することができます。

ViewController.swift
override func viewDidLoad() {
    super.viewDidLoad()
    guard let fl = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout else { return }
    fl.headerReferenceSize = CGSize(width: self.view.bounds.width, height: 30)
}

スクリーンショット 2017-11-12 0.32.33.png

セクションによってヘッダの高さを変えたい場合はUICollectionViewDelegateFlowLayoutプロトコルを適用させた上で次のメソッドを追加すると高さを調節することができます。

ViewController.swift
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)を返します。

ViewController.swift
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)
    }
}

スクリーンショット 2017-11-12 0.45.25.png

ヘッダを画面上部に固定する

上にスクロールさせた時にセクションヘッダを画面上部に固定する場合は以下のようにviewDidLoadにコードを追加します(これを書かずに今まで通りのコードを書いていれば画面上部に固定されません)。

ViewController.swift
override func viewDidLoad() {
    super.viewDidLoad()
    guard let fl = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout else { return }
    fl.sectionHeadersPinToVisibleBounds = true
}

スクリーンショット 2017-11-12 1.00.09.png

xibで作ったヘッダを設定する

ヘッダはこんなふうに作りました。

スクリーンショット 2017-11-12 22.15.56.png

XibHeader.swift
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に以下のコードを足しています。

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()
}

スクリーンショット 2017-11-12 22.13.44.png

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
72
Help us understand the problem. What are the problem?