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

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

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

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

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

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

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

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

```swift
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.規定の文字列を渡すコンストラクタで作る

```swift
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つのビューが対象なのでシンプルですが、
複数のビューが複雑に絡みだすと
メンテナンスするときにわかりづらいし、なおしづらいからです

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

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

```swift
/// 制約(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`用の引数 `attr`と`attrTo`はメソッドラベルを省けるようにしています。
どうせ制約対象の`item`もしくは`to`の属性は必要なんだから・・・という考え方です

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

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