Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

役に立つかどうかよくわからない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でピタピタと制約を貼っていくのと、それほど変わらず直感的で労力少なめに出来る気がしませんか?

nakau1
PHP / iOS 技術者としてやってきましたが、 自分がやってきたお仕事以外では、色々と技術が足りないと痛感しながら 基本的なことからコツコツ勉強しております
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした