アトラシエの開発ブログ

株式会社アトラシエのブログです

UICollectionViewのHeaderをxibから出し分ける

UICollectionView vs UITableView

UITableViewのtableHeaderViewのように、UICollectionViewの上端にheaderを入れたい時、collectionView:viewForSupplementaryElementOfKind:atIndexPath:というメソッドでsectionの先頭にヘッダーを追加すると、UITableViewとは違ってスクロール時に固定されることがない、tableHeaderViewのような見た目を作ることができます。

ただしこの方法で動的にheaderの出し分けを行いたいときは少し工夫が必要です。なぜならstoryboardではsectionに複数のviewを登録しておくことができないためです。

出し分けるview(UICollectionReusableView)をxibで定義する

xibで何種類かのheaderを作成しておきます。

f:id:attracie:20151009075311p:plain

このとき、特にこだわり・実装上の都合がなければ

  • 出し分けるxibの名前
  • (必要があれば)xibに対応するUICollectionReusableViewのサブクラス名
  • collectionViewのreusableIdentifier
  • UICollectionViewを管理するクラスで、どのヘッダーを使うかの識別子

を全て同じEnum(文字列)で表現するとわかりやすくなります。

ViewControllerのコード

class CollectionViewController : UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
    enum ListType : String {
        case Default = "CatalogListDefaultHeaderView"
        case Bookmark = "CatalogListBookmarkHeaderView"
        case Popular = "CatalogListPopularHeaderView"
        case Recent = "CatalogListRecentHeaderView"
    }

    var listType : ListType = .Default

    override func viewDidLoad() {
        super.viewDidLoad()

        for type in AllListType {
            let nib = UINib(nibName: type.rawValue, bundle: nil)
            collectionView.registerNib(nib, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: type.rawValue)
        }
        
         self.flowLayout.headerReferenceSize = CGSize(width: collectionView.frame.width, height: 100)
    }

    func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {
        var reusableView : UICollectionReusableView!
        
        if kind == UICollectionElementKindSectionHeader {
            reusableView = collectionView.dequeueReusableSupplementaryViewOfKind(kind, withReuseIdentifier: listType.rawValue, forIndexPath: indexPath)
        }
        
        return reusableView
    }
}

全体像はざっとこうなるのですが、ポイントはいくつかあります。

qiita.com

だいたいこちらの記事に似たような内容があるのですが、

  • headerReferenceSizeに値を入れないとviewForSupplementary...が呼ばれないこと
  • viewForSupplementary...で登録されていないviewをいきなり返しても実行時エラーになること
  • 表示のタイプをString側のenumで定義しておくと管理が楽

というあたりがポイントです。

UICollectionViewはtableのように使えたり表現力がとても豊かで個人的に好きです。