【swift】Viewに自分自身の大きさを定義して、動的なViewを正しくレイアウトする

動的に要素を並べるようなViewを作成した時に、AutoLayoutの設定の仕方で悩みました。

class CustomView: UIView {

    init() {
        super.init(frame: .zero)

        backgroundColor = .whiteColor()

        // 正方形のViewを左右に並べる
        let view1 = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
        let view2 = UIView(frame: CGRect(x: 110, y: 0, width: 100, height: 100))

        view1.backgroundColor = .blueColor()
        view2.backgroundColor = .greenColor()

        addSubview(view1)
        addSubview(view2)
    }
}

このようにinitializeの中で動的に要素を作成しているViewを作成します。
このViewの中ではAutoLayoutは定義せずにCGRectで位置を設定しています。

CustomViewを表示してみる

このViewをViewControllerでaddSubviewしてあげます。
addしたCustomViewはAutoLayoutで中央揃えになるように設定します。

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .grayColor()

        let customView = CustomView()
        view.addSubview(customView)
        customView.translatesAutoresizingMaskIntoConstraints = false

        // customView自体には中央揃えになるようにAutoLayoutを設定
        customView.centerYAnchor.constraintEqualToAnchor(view.layoutMarginsGuide.centerYAnchor).active = true
        customView.centerXAnchor.constraintEqualToAnchor(view.layoutMarginsGuide.centerXAnchor).active = true
    }

}

f:id:w6500:20160524110915p:plain:w300

ビルドしてみるとこんな感じです。
うまく中央揃えになりません。
また、CustomView自体の白背景部分が描画されていないため、CustomViewに大きさが無いことがわかります。

intrinsicContentSizeの設定

UIViewにはintrinsicContentSizeというCGSizeを返すメソッドが定義されており、そのサイズを自分自身のサイズと認識します。

ですので、先ほどのCustomViewにintrinsicContentSizeをoverrideさせます。

class CustomView: UIView {

    init() {
        super.init(frame: .zero)

        backgroundColor = .whiteColor()

        let view1 = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
        let view2 = UIView(frame: CGRect(x: 110, y: 0, width: 100, height: 100))

        view1.backgroundColor = .blueColor()
        view2.backgroundColor = .greenColor()

        addSubview(view1)
        addSubview(view2)
    }
    
    // 追加
    override func intrinsicContentSize() -> CGSize {
        return CGSize(width: 210, height: 100)
    }
}

これでCustomView自体は、幅210px、高さ100pxの要素だと認識するようになりました。

この状態でビルドしてみると

f:id:w6500:20160524112001p:plain:w300

正しく中央揃えになり、CustomViewの白背景も描画されています。

今回の例はサイズを全て決め打ちで書いていますが、この辺りをちゃんと計算して算出するようにすれば、動的な要素も正しくレイアウトされるようになります。