Edited at

コードでAutolayout

More than 3 years have 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のこと