UICollectionViewでSelfSizingを行う


UICollectionViewのsizeForItemAtIndexPathで高さを動的に設定(systemLayoutSizeFittingSize)していると、
セルの数が増えるにつれ動作がもっさりしてきたので、代わりになるものとしてSelfSizingを使ってみます。

今回は、セルの最大横幅あり、高さ可変で表示します。
ポイントは以下の4つです。

  1. estimatedItemSize(凡そのセルサイズ)を設定し、SelfSizingを有効にします
  2. 最大横幅を設定します(横幅が固定値であればAutoLayoutでWidthを設定するだけ?)
  3. AutoLayoutを上下左右に設定します(どこかが抜けると正しく表示されませんでした)
  4. 最大横幅に合わせて文字を折り返すようにLinesを0に設定します

実行結果

上記のような画面を作っていきます。

SelfSizing有効化

estimatedItemSize(凡そのセルサイズ)を設定し、SelfSizingを有効にします。
実際のセルサイズに近い値を設定したほうがよさそうな気はしますが、とりあえずw1h1にしています。
w0h0にするとSelfSizingは有効になりません。

ViewController
import UIKit

class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {

    @IBOutlet var collectionView: UICollectionView!

    override func viewDidLoad()
    {
        super.viewDidLoad()

        // Self-Sizingの有効化
        if let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
            flowLayout.estimatedItemSize = CGSizeMake(1, 1)
        }
    }

    // MARK: UICollectionViewDataSource
    func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
    {
        return 100
    }

    func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
    {
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! LabelCollectionViewCell
        cell.configureWithIndexPath(indexPath)
        return cell
    }
}

最大横幅設定

ラベルの最大横幅を設定します。

LabelCollectionViewCell
import UIKit

class LabelCollectionViewCell: UICollectionViewCell {

    @IBOutlet weak var label: UILabel!

    func configureWithIndexPath(indexPath: NSIndexPath)
    {
        // 枠組み
        layer.borderWidth = 1
        layer.borderColor = UIColor.blackColor().CGColor
        backgroundColor = UIColor(red: 0.9, green: 0.9, blue: 0.9, alpha: 1.0)

        // テキスト作成
        label.text = labelTextWithNum(indexPath.item + 1)

        // 最大横幅設定 これが無いとエラーログが延々と…
        label.preferredMaxLayoutWidth = 50 //CGRectGetWidth(label.frame)
    }

    private func labelTextWithNum(num: Int) -> String
    {
        var text = "1"
        if num > 1 {
            for t in 2...num {
                text = text.stringByAppendingString(" \(t)")
            }
        }
        return text
    }
}

AutoLayout設定

上下左右全てにAutoLayoutを設定します。

最大横幅に合わせて折り返し表示

ラベルのLinesを0に設定します。

最後に

systemLayoutSizeFittingSizeでセルサイズを計算するより早いのかまだ検証してません。

参考

http://qiita.com/yuya_presto/items/08b0656f67a59c8c2d03
http://stackoverflow.com/questions/25947146/multiple-uilabels-inside-a-self-sizing-uitableviewcell