More than 1 year has passed since last update.

以前からAutolayoutの重要性や便利さをちらちら目にしてきましたがとっつきにくさからずっと避けてました。
そろそろ身につけなきゃとうことでレシピをまとめたいと思います。

参考

http://www.slideshare.net/classmethod/i-os-auto-layout
を参考にさせて頂きました。

Autolayoutとは

AutolayoutとはviewにConstraint(制約)を付けることでレイアウトする方法。
各view同士を5px間隔で配置する、など画面の大きさにとらわれず最適なviewの配置をします。
制約はNSLayoutConstraintクラスによって定義でき、制約には優先度を設定できます。

NSLayoutConstraint

NSLayoutConstraintは2つのコンストラクタがあります。

constraintsWithVisualFormat:options:metrics:views

こっちの方が複数のviewの制約を一度に定義できるので便利そう。
だが、visualFormatの書き方がとっつきにくいのでAutolayoutについて理解が深まってから。
例えば@"V:|-8-[button1]"みたいにV(縦)方向の定義で
「上8px隙間を開けてbutton1を配置する」といった具合。
今回はこちらは使わない

constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant:

こちらは1つのviewに対して制約を定義していく。
itemtoItemに対して制約を定義して他の引数で制約を調整する。
今回はこっちを使ってNSLayoutConstraintの使い方を学んでいく。

arg description
item 制約を追加するview、このviewに対して第2引数のattributeで指定した属性を反映させる
attribute 属性
relateBy 計算式の関係性
toItem 位置計算に使用するview
attribute 位置計算に使用する属性、値の取得元はtoItem
multiplier 乗算値
constant 定数値

制約

制約はNSLayoutConstraintでview同士やviewに対して制約を生成して親viewにaddConstraintsしていく。
viewControllerに子viewのaViewがあるとすると

       self.view.addConstraints([

            NSLayoutConstraint(
                item: self.aView,
                attribute: NSLayoutAttribute.Top,
                relatedBy: NSLayoutRelation.Equal,
                toItem: self.view,
                attribute: NSLayoutAttribute.Top,
                multiplier: 1.0,
                constant: 0
            )]
        )

のようにself.viewに子viewであるself.aViewについての制約を追加していく

制約を考える上で

制約とは計算式で表されているみたい。
例えば「view1の右端から右に10pxの位置にview2の左端が位置する」ってのを書きたいとき

view2.left = view1.right * 1 + 10

(item).(attribute1) (relatedBy) (toItem).(attribute2) * (multiplier) + (constant)

relatedBy=を表す。NSLayoutRelation.Equalみたいな。

となる。これを想像しながら制約を作っていくとイメージしやすそう

コードレシピ

コード書いてみないと覚えらんないので色んな制約を試してみました。

画面上部に引っ付いている、横幅いっぱいのnavigatoinView

        self.navigationView.setTranslatesAutoresizingMaskIntoConstraints(false)

        self.view.addConstraints([

            // self.veiwの上から0pxの位置に配置
            NSLayoutConstraint(
                item: self.navigationView,
                attribute: NSLayoutAttribute.Top,
                relatedBy: NSLayoutRelation.Equal,
                toItem: self.view,
                attribute: NSLayoutAttribute.Top,
                multiplier: 1.0,
                constant: 0
            ),

            // self.viewの横幅いっぱいにする
            NSLayoutConstraint(
                item: self.navigationView,
                attribute: NSLayoutAttribute.Width,
                relatedBy: NSLayoutRelation.Equal,
                toItem: self.view,
                attribute: NSLayoutAttribute.Width,
                multiplier: 1.0,
                constant: 0
            ),

            // self.viewのレイアウトに関わらず高さは64px
            NSLayoutConstraint(
                item: self.navigationView,
                attribute: NSLayoutAttribute.Height,
                relatedBy: NSLayoutRelation.Equal,
                toItem: nil,
                attribute: NSLayoutAttribute.Height,
                multiplier: 1.0,
                constant: 64
            )]
        )

navigationViewの上から10px左から10pxの位置にあり、縦横44pxのleftButton

        self.leftButton.setTranslatesAutoresizingMaskIntoConstraints(false)

        self.navigationView.addConstraints([

            // 上から10px
            NSLayoutConstraint(
                item: self.leftButton,
                attribute: .Top,
                relatedBy: .Equal,
                toItem: self.navigationView,
                attribute: .Top,
                multiplier: 1.0,
                constant: 10
            ),

            // 左から10px
            NSLayoutConstraint(
                item: self.leftButton,
                attribute: .Left,
                relatedBy: .Equal,
                toItem: self.navigationView,
                attribute: .Left,
                multiplier: 1.0,
                constant: 10
            ),

            // 横(固定)
            NSLayoutConstraint(
                item: self.leftButton,
                attribute: .Width,
                relatedBy: .Equal,
                toItem: nil,
                attribute: .Width,
                multiplier: 1.0,
                constant: 44
            ),

            // 縦(固定)
            NSLayoutConstraint(
                item: self.leftButton,
                attribute: .Height,
                relatedBy: .Equal,
                toItem: nil,
                attribute: .Height,
                multiplier: 1.0,
                constant: 44
            )]
        )

navigationViewの中央にある、横幅がnavigationViewの横幅によって動的に変化するcenterView

横幅320pxの時、navigationView.width:320px、centerView.width:100pxの比率を保ような横幅

        self.centerView.setTranslatesAutoresizingMaskIntoConstraints(false)

        self.navigationView.addConstraints([

            // centerViewのcenter.xをnavigationViewと同じに
            NSLayoutConstraint(
                item: self.centerView,
                attribute: .CenterX,
                relatedBy: .Equal,
                toItem: self.navigationView,
                attribute: .CenterX,
                multiplier: 1.0,
                constant: 0
            ),

            // centerViewのcenter.yをnavigationViewと同じに
            NSLayoutConstraint(
                item: self.centerView,
                attribute: .CenterY,
                relatedBy: .Equal,
                toItem: self.navigationView,
                attribute: .CenterY,
                multiplier: 1.0,
                constant: 0
            ),

            // navigationView.width:320のときcenterView:100の比率
            NSLayoutConstraint(
                item: self.centerView,
                attribute: .Width,
                relatedBy: .Equal,
                toItem: self.navigationView,
                attribute: .Width,
                multiplier: 100.0 / 320.0,
                constant: 0
            ),

            // 縦は44pxで固定
            NSLayoutConstraint(
                item: self.centerView,
                attribute: .Height,
                relatedBy: .Equal,
                toItem: nil,
                attribute: .Height,
                multiplier: 1.0,
                constant: 44
            )]
        )

centerViewの右から20pxのところに位置する縦横44pxのrightButton

        self.rightButton.setTranslatesAutoresizingMaskIntoConstraints(false)

        self.navigationView.addConstraints([

            // centerViewの右から20pxのところに配置
            NSLayoutConstraint(
                item: self.rightButton,
                attribute: .Left,
                relatedBy: .Equal,
                toItem: self.centerView,
                attribute: .Right,
                multiplier: 1.0,
                constant: 20
            ),

            // center.yはcenterViewと同じ
            NSLayoutConstraint(
                item: self.rightButton,
                attribute: .CenterY,
                relatedBy: .Equal,
                toItem: self.centerView,
                attribute: .CenterY,
                multiplier: 1.0,
                constant: 0
            ),

            // 横(固定)
            NSLayoutConstraint(
                item: self.rightButton,
                attribute: .Width,
                relatedBy: .Equal,
                toItem: nil,
                attribute: .Width,
                multiplier: 1.0,
                constant: 44
            ),

            // 縦(固定)
            NSLayoutConstraint(
                item: self.rightButton,
                attribute: .Height,
                relatedBy: .Equal,
                toItem: nil,
                attribute: .Height,
                multiplier: 1.0,
                constant: 44
            )]
        )

おしまい

autolayoutの超基本を触れたのであとは深堀りをしていく。
このコードをロジックと同じクラスに書くと見通しが悪くなるのでレイアウトは別クラスにするとか設計を含めて学んでいきたい。

メモ

NSLayoutRelationの種類別
NSLayoutAttributeLeftMarginとか
優先度の使い方
constraintsWithVisualFormat:options:metrics:viewsのこと