LoginSignup
35
30

More than 5 years have passed since last update.

NSLayoutConstraintをプログラムでシンプルに書く

Last updated at Posted at 2016-03-22

役に立つかどうかよくわからないTipsシリーズ

storyboard や xib で AutoLayout として設定する時に
大事なのは「Constraint」
レイアウト制約です

objective-c 時代から苦手な人も多いようで
一番最初は自分も戸惑いが大きかったですが
慣れてみるとこんなに便利なものはありません

本来であれば、IB上ですべて済ませられれば理想的ですが
動的にビューを生成したりするときには
プログラマブルに書いていく必要があります

NSLayoutConstraintを生成するのは、2通りのやり方があります

ここでの例は
hogeViewというViewを、self.viewの上部にぴったり44pxの高さで配置する」
(ここでいうselfはViewControllerとする)
という表示を期待しています

1.引数たっぷりのコンストラクタで作る

self.view.addConstraints(
    [
        NSLayoutConstraint(
            item:       hogeView,
            attribute:  .Top,
            relatedBy:  .Equal,
            toItem:     self.view,
            attribute:  .Top,
            multiplier: 1.0,
            constant:   0
        ),
        NSLayoutConstraint(
            item:       hogeView,
            attribute:  .Leading,
            relatedBy:  .Equal,
            toItem:     self.view,
            attribute:  .Leading,
            multiplier: 1.0,
            constant:   0
        ),
        NSLayoutConstraint(
            item:       hogeView,
            attribute:  .Trailing,
            relatedBy:  .Equal,
            toItem:     self.view,
            attribute:  .Trailing,
            multiplier: 1.0,
            constant:   0
        ),
        NSLayoutConstraint(
            item:       hogeView,
            attribute:  .Height,
            relatedBy:  .Equal,
            toItem:     nil,
            attribute:  .Height,
            multiplier: 1.0,
            constant:   44.0
        ),
    ]
)

2.規定の文字列を渡すコンストラクタで作る

let views = ["hoge": hogeView]

self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(
    "H:|-0-[hoge]-0-|",
    options: NSLayoutFormatOptions(),
    metrics: nil,
    views: views
    )
)
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(
    "V:|-0-[hoge(44)]",
    options: NSLayoutFormatOptions(),
    metrics: nil,
    views: views
    )
)

まぁ、この辺は好みなのですが、後者はどうにも好きになれません

上の例では1つのビューが対象なのでシンプルですが、
複数のビューが複雑に絡みだすと
メンテナンスするときにわかりづらいし、なおしづらいからです

しかしながら、前者の引数たっぷりのコンストラクタは
ソースが冗長になりすぎます

なので、このような関数をトップレベルに作ると便利になります

/// 制約(NSLayoutConstraint)を生成する
/// - parameter item: 制約を追加するオブジェクト
/// - parameter attr: 制約を追加するオブジェクトに与える属性
/// - parameter to: 制約の相手
/// - parameter attrTo: 制約相手に使用する属性
/// - parameter constant: 定数値
/// - parameter multiplier: 乗数値
/// - parameter relate: 計算式の関係性
/// - parameter priority: 制約の優先度
/// - returns: 制約(NSLayoutConstraint)オブジェクト
public func Constraint(item: AnyObject, _ attr: NSLayoutAttribute, to: AnyObject?, _ attrTo: NSLayoutAttribute, constant: CGFloat = 0.0, multiplier: CGFloat = 1.0, relate: NSLayoutRelation = .Equal, priority: UILayoutPriority = UILayoutPriorityRequired) -> NSLayoutConstraint {
    let ret = NSLayoutConstraint(
        item:       item,
        attribute:  attr,
        relatedBy:  relate,
        toItem:     to,
        attribute:  attrTo,
        multiplier: multiplier,
        constant:   constant
    )
    ret.priority = priority
    return ret
}

「うわっ、なにこれ? 引数たっぷりじゃん」
ってなりましたか?

しかしながら、引数constant以降はすべて省略可能な引数です

IB上で AutoLayout を触る時を想定したときに
それほど変更することが少ないものは省略可能としておくことで、書くことを少なくしています
必要なものだけ引数に追加するイメージですね

さらに、属性をあらわすNSLayoutAttribute用の引数 attrattrToはメソッドラベルを省けるようにしています。
どうせ制約対象のitemもしくはtoの属性は必要なんだから・・・という考え方です

これにより、下記のようにソースを書くことができます

self.view.addConstraints([
    Constraint(hogeView, .Top,    to: self.view, .Top),
    Constraint(hogeView, .Bottom, to: self.view, .Bottom),
    Constraint(hogeView, .Left,   to: self.view, .Left),
    Constraint(hogeView, .Width,  to: nil,       .Width, constant: 44),
])

これで例と同じ表示をしてくれるようになります
シンプルになりましたね

IBでピタピタと制約を貼っていくのと、それほど変わらず直感的で労力少なめに出来る気がしませんか?

35
30
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
35
30