コンテンツに応じてサイズが自動的に変わるカスタムビューを作る

カスタムビューを作っていると、コンテンツに応じてサイズが自動的に変わってほしいことがある。そういう時は、intrinsicContentSizeメソッドをオーバライドする。
例えば、UILabelを拡張してPaddingを設定できるラベルを作る場合を考える。
スクリーンショット 2015-05-23 16.18.16
標準のUILabelは、AutoLayoutが有効な状態では自動的に内部の文字列に応じて大きさが変わる。UILabelには内側余白を指定する機能はないので、これを実現したい場合はカスタムビューを作る必要がある。

class CustomLabel: UILabel {
    var padding: CGFloat = 0
    override func drawTextInRect(rect: CGRect) {
        let newRect = CGRectInset(rect, padding, padding)
        super.drawTextInRect(newRect)
    }
    override func intrinsicContentSize() -> CGSize {
        let originalSize = super.intrinsicContentSize()
        return CGSizeMake(originalSize.width + padding * 2, originalSize.height + padding * 2)
    }
}

結論から言うと、冒頭にも書いた通りintrinsicContentSizeメソッドをオーバーライドすることで、ビューの大きさを指定することができる。上のコードでは、オリジナルのサイズ(super.intrinsicContentSize()が返す値 = UILabel本来の文字列に応じて計算された大きさ)に余白(padding * 2)をつけたサイズを返している。(drawTextInRectをオーバーライドしているのは、文字列が左寄りではなく余白を踏まえて中央に描画されるようにするためで、カスタムビューのサイズ計算への影響はない)
intrinsicContentSizeでサイズを指定しても、AutoLayoutが有効でない場合やAutoLayoutの設定と矛盾する(画面に入りきらない、など)場合は、Content Hugging PriorityやContent Compression Resistance Priorityに基づいて大きさが調整されることになる。
なお、intrinsicContentSizeの値はキャッシュされる。内容に変更があり、ビューのサイズを変更する必要がある場合は、invalidateIntrinsicContentSize()メソッドを呼んでやると、レイアウトが更新される。Swiftを使っている場合はプロパティのdidSetの中で呼んでやるのが便利。

var padding: CGFloat = 0 {
    didSet {
        invalidateIntrinsicContentSize()
    }
}

参考

Pocket

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です