コードでSwiftのautolayoutを書く

Storyboardで画面のレイアウトやUI部品をグラフィカルに配置できますが、チームで協同作業する時や細かいレイアウトの調整が発生する場合はStoryboardだとどうしても支障が出ます。

autolayoutの標準APIについて、Appleが`NSLayoutConstraint`と`NSLayoutAnchor`(ios9~)、`Visual Format Language(VFL)`を提供しています。`NSLayoutAnchor`は`NSLayoutConstraint`よりコード量が少なくなり、制約・声明もはっきりするようになっています。`VFL`はアスキーアートのように制約を定義することができる記法ですが、学習コストが高くかつリテラルな箇所が多いため、実行時にエラーが起こる恐れがあります(本文では`VFL`パターンを紹介しません)。

##1、NSLayoutConstraint
>NSLayoutConstraintを使用するには、各々初期化する必要があります。
制約する属性ごとに`NSLayoutConstraint`を作る必要があるため、コード量が増えます。
初期化メソッド：`NSLayoutConstraint.init()`

```swift:
//UIImageViewを作成し、viewに追加する
let layoutIcon = "layoutIcon"
let layoutImg = UIImage(named: layoutIcon)
let layoutView = UIImageView(image: layoutImg)
layoutView.backgroundColor = .green
layoutView.layer.cornerRadius = 10
//AutoresizingMaskをAutoLayoutの制約に置き換えるかどうか指定する値(必ずfalse)
layoutView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(layoutView)
        
//制約を追加　top:50 
//パラメーター説明 item:制約オブジェクト　attribute:制約する属性　relatedBy:制約タイプ　toItem:制約の相手 attribute:制約相手に使用する属性 multiplier:乗数値 constant:定数値
let topConstraint = NSLayoutConstraint.init(item: layoutView, attribute: .top, relatedBy: .equal, toItem: view, attribute: .top, multiplier: 1.0, constant: 50.0)
//制約は定義した後、activateする必要あり
topConstraint.isActive = true
        
//制約を追加　left:50
let leftConstraint = NSLayoutConstraint.init(item: layoutView, attribute: .left, relatedBy: .equal, toItem: view, attribute: .left, multiplier: 1.0, constant: 50.0)
leftConstraint.isActive = true
        
//制約を追加　width:70
let widthConstraint = NSLayoutConstraint.init(item: layoutView, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 70.0)
widthConstraint.isActive = true
        
//制約を追加　height:70
let heightConstraint = NSLayoutConstraint.init(item: layoutView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 70.0)
heightConstraint.isActive = true
```
|<img src= "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/678085/c9ba43e8-41bd-34be-4f85-9d3f2199cdb0.png" height=350 width=250>|
|:--:|




##2、NSLayoutAnchor
>iOS9からは`NSLayoutAnchor`を使い`NSLayoutConstraint`オブジェクトを作ることができました。ただ直接に`NSLayoutConstraint`オブジェクトを作ることではなく、`UIView`あるいはその子クラスや`UILayoutGuide`のある`anchor`プロパティー値を利用します。これらのプロパティーはautolayout中の主要`NSLayoutAttribute`に相当します。
そのため、`NSLayoutAnchor`の子クラスで`NSLayoutAttribute`を設定してもいいです。
制約メソッド：`xxxView.xxxAnchor.constraint( )`

```swift:
//紫色のUIImageViewを作成し、viewに追加する
let purLayoutIcon = "layoutIcon"
let purLayoutImg = UIImage(named: purLayoutIcon)
let purLayoutView = UIImageView(image: purLayoutImg)
purLayoutView.backgroundColor = .purple
purLayoutView.layer.cornerRadius = 10
//AutoresizingMaskをAutoLayoutの制約に置き換えるかどうか指定する値(必ずfalse)
purLayoutView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(purLayoutView)
        
//制約を追加 top:150 left:50 width:70 height:70
purLayoutView.topAnchor.constraint(equalTo: view.topAnchor, constant: 150.0).isActive = true
purLayoutView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 50.0).isActive = true
purLayoutView.widthAnchor.constraint(equalToConstant: 70.0).isActive = true
purLayoutView.heightAnchor.constraint(equalToConstant: 70.0).isActive = true
```

|<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/678085/d0b55e6a-c775-eb8d-ec69-8c0021eef998.png height=350 width=250>|
|:--:|

`NSLayoutConstraint`より`NSLayoutAnchor`のほうは簡潔です。

##3、ScrollView中のautolayout
>`ScrollView`はセルフの`contentSize`があるため、autolayout使用時に留意したほうがいいです。
`ScrollView`の子viewのサイズは即ち`contentSize`です。
制約は`ScrollView`の完全なレイアウトを決める必要があり、上下左右ともに必要です。また、`ScrollView`の子viewの最大サイズも必ず制約条件を通じて確定値を得られます。

```swift:
//scrollViewを作成し、viewに追加する
let scrollView = UIScrollView()
scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.backgroundColor = .cyan
//ページ単位のスクロールを可能する
scrollView.isPagingEnabled = true
view.addSubview(scrollView)
scrollView.topAnchor.constraint(equalTo: purLayoutView.bottomAnchor, constant: 30.0).isActive = true
scrollView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 0).isActive = true
//safeAreaのボトム距離を使う
scrollView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 0).isActive = true
scrollView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 0).isActive = true
        
let bgColor = [UIColor.red,UIColor.green,UIColor.blue]
        
var leftView:UIView? = nil
    for i in 0..<3{
          let view = UIView()
          view.translatesAutoresizingMaskIntoConstraints = false
          view.backgroundColor = bgColor[i]
          scrollView.addSubview(view)
          if let left = leftView{
            view.leftAnchor.constraint(equalTo: left.rightAnchor, constant: 0).isActive = true
          }else{
            view.leftAnchor.constraint(equalTo: scrollView.leftAnchor, constant: 0).isActive = true
          }
          view.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 0).isActive = true
          view.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 0).isActive = true
          view.widthAnchor.constraint(equalTo: scrollView.widthAnchor, multiplier: 1.0).isActive = true
          view.heightAnchor.constraint(equalTo: scrollView.heightAnchor, multiplier: 1.0).isActive = true
          leftView = view
        }
        //全ての子viewは上、下、左がscrollViewを相手として制約していたが、右のほうがまだ
        //右方向の制約を追加
        if let left = leftView{
          left.rightAnchor.constraint(equalTo: scrollView.rightAnchor, constant: 0).isActive = true
        }
```

<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/678085/a140b92d-224e-b0f4-addd-90bb1118f0ea.png>