コードでAutoLayout-入門

これまで何時代の人だよという感じでframeで座標合わせしてましたが、最近ようやくAutoLayoutを触り出しました
でもStoryboardは使わず基本ソースコード上で扱い、残念(？)ながらNSLayoutConstraintはあんまり理解しないまま[TinyConstraints](https://github.com/roberthein/TinyConstraints)というオープンソース触ってます
[SnapKit](https://github.com/SnapKit/SnapKit)などがメジャーらしいですが、触った感じ[TinyConstraints](https://github.com/roberthein/TinyConstraints)に落ち着きました
ここではその基本的な使い方を載せます

基本は[README](https://github.com/roberthein/TinyConstraints#examples)や[こちら](https://dev.classmethod.jp/smartphone/tinyconstraints/)に書いてる通りですけど詳細に書いたつもりです


#環境
|名前|バージョン|
|:---|:-----|
|TinyConstraints|3.1.0|
|iOS|10以上|
|Swift|4.0|
|Simulator|iPhone SE, iPhone X|


#大きさに関する制約


##指定したviewと同じサイズ
```swift
let subView = UIView()
self.view.addSubview(subView)
//エッジを指定したビューに合わせる(今回は親ビュー)
subView.edges(to: self.view) 
subView.backgroundColor = .red
```
上の場合のように親ビューと同じサイズにしたい場合は専用のメソッドがあります

```swift
//エッジを親ビューに合わせる
subView.edgesToSuperview()
```

どちらも同じでこうなります
<img src="https://i.gyazo.com/1534ee5e65091d02d16f5d6511092895.png" width=50%>


##余白をつける

```swift
//親ビューを緑にする
self.view..backgroundColor = .green

let subView = UIView()
self.view.addSubview(subView)
//エッジを親ビューに合わせる
subView.edgesToSuperview(insets: TinyEdgeInsets(top: 20, left: 20, bottom: 20, right: -20))
subView.backgroundColor = .red
```
`TinyEdgeInsets`というのが出てきましたがただの`UIEdgeInsets`の`typealias`です

<img src="https://i.gyazo.com/0148784cd9fc618259d214ef39632893.png" width=50%>

##[SafeArea](https://qiita.com/gentlejkov/items/a626263d452939378b07)に入らない大きさにする

ぶっちゃけこのためにTinyConstraintsを触ろうと思ったやつです
**usingSafeAreaをtrue**にしたら[SafeArea](https://qiita.com/gentlejkov/items/a626263d452939378b07)を考慮してレイアウトしてくれます

```swift
subView.edgesToSuperview(usingSafeArea: true)
```
<img src="https://i.gyazo.com/9c9c4eac7c1e3d22cb920bafde7a8934.png" width=50%>

##大きさを比率で指定
```swift
//サイズを指定したビューの0.5倍にする
subView.size(to: self.view, multiplier: 0.5)
//centerを親ビューに合わせる
subView.centerInSuperview()
```

`width`と`height`を別々に指定することもできます

```swift
subView.width(to: self.view, multiplier: 0.5)
subView.height(to: self.view, multiplier: 0.5)
subView.centerInSuperview()
```

親ビューに対する比率なので端末ごとに赤色のサイズは変わります

<img src="https://i.gyazo.com/b218d6c8f78cc6ac5310e27d14c1ccc3.png" width=50%>

##幅と高さを固定値で指定

固定の数値を入れるだけです

```swift
subView.width(100)
subView.height(200)
subView.centerInSuperview()
```
<img src="https://i.gyazo.com/049cdc2ed1f6a6c7432cc19878d3ab9e.png" width=50%>

##高さを指定したviewの横幅にする
自身の横幅に合わせると正方形になります


```swift
//幅と高さを親ビューの幅にする
subView.widthToSuperview()
subView.height(to: subView, subView.widthAnchor)
subView.centerInSuperview()

```

同様に指定したビューの高さの制約に合わせたいなら `subView.heightAnchor`を指定すればいいです

<img src="https://i.gyazo.com/044d73e6d5e09859569d62b7dfc6af61.png" width=50%>



#位置に関する制約

##指定したビューのセンターに合わせる

```swift
let subView = UIView()
self.view.addSubview(subView)
subView.width(100)
subView.height(to: subView, subView.widthAnchor)
//センターを指定したビューのセンターに合わせる
subView.center(in: view)
subView.backgroundColor = .red
```

上のように親ビューのセンターに合わせるならこう書けます

```swift
subView.centerInSuperview()
```

X座標とY座標を個別に親ビューのセンターに合わせるならこう書けます

```swift
subView.centerXToSuperview()
subView.centerYToSuperview()
```

<img src="https://i.gyazo.com/898b77835cee1c375063d449b5c4d587.png" width=50%>

##上下左右の合わせ方

書くのが面倒になってきたので一気に6つ載せます

```swift

  override func viewDidLoad() {
    super.viewDidLoad()
    self.setup()
  }
  
  func createSubView(superView:UIView, color:UIColor = .red)->UIView{
    let subView = UIView()
    superView.addSubview(subView)
    subView.width(100)
    subView.height(to: subView, subView.widthAnchor)
    subView.backgroundColor = color
    return subView
  }
  
  func setup(){
    
    self.view.backgroundColor = .green

    //左上
    do{
      let subView = createSubView(superView: self.view)
      subView.leftToSuperview()
      subView.topToSuperview()
    }
    //左真ん中
    do{
      let subView = createSubView(superView: self.view)
      subView.leftToSuperview()
      subView.centerYToSuperview()
    }
    //左下
    do{
      let subView = createSubView(superView: self.view)
      subView.leftToSuperview()
      subView.bottomToSuperview()
    }
    //真ん中上
    do{
      let subView = createSubView(superView: self.view)
      subView.topToSuperview()
      subView.centerXToSuperview()
    }
    //真ん中
    do{
      let subView = createSubView(superView: self.view)
      subView.centerInSuperview()
    }
    //真ん中下
    do{
      let subView = createSubView(superView: self.view)
      subView.bottomToSuperview()
      subView.centerXToSuperview()
    }
    //右上
    do{
      let subView = createSubView(superView: self.view)
      subView.topToSuperview()
      subView.rightToSuperview()
    }
    //右真ん中
    do{
      let subView = createSubView(superView: self.view)
      subView.centerYToSuperview()
      subView.rightToSuperview()
    }
    //右下
    do{
      let subView = createSubView(superView: self.view)
      subView.bottomToSuperview()
      subView.rightToSuperview()
    }
  }
```

<img src="https://i.gyazo.com/6a3672bbba5bf02f1cad3e1d33262673.png" width=50%>

##safeAreaに入らない位置に配置する

例によってこれが便利だと思ってます
上の例でsafeAreaに入らないようにします

```swift
//左上
do{
  let subView = createSubView(superView: self.view)//自作メソッド
  subView.leftToSuperview(usingSafeArea:true)
  subView.topToSuperview(usingSafeArea:true)
}
//以下同様にusingSafeArea:trueを指定するだけ
```

<img src="https://i.gyazo.com/dfe5be23af8755fcb5616d99425717a2.png" width=50%>


##オフセットを入れる

```swift
//自身のサイズの高さと幅の半分ずつずらす
let subView = createSubView(superView: self.view)//自作メソッド
subView.leftToSuperview(offset: subView.constraints.first!.constant/2)
subView.topToSuperview(offset: subView.constraints.first!.constant/2, usingSafeArea:true)
```
しれっと書きましたが`subView.constraints.first!.constant`で制約につけた数値を取得できます

<img src="https://i.gyazo.com/2cc55361d1555a48feb37d472d3d61ae.png" width=50%>


##周りのviewにくっつける
```swift
//真ん中
let centerView = createSubView(superView: self.view)
centerView.centerInSuperview()

//真ん中のviewの右側にくっつける
let rightView = createSubView(superView: self.view, color: .blue)
rightView.leftToRight(of: centerView)
rightView.centerY(to: centerView)
    
//真ん中のviewの下側にくっつける
let bottomView = createSubView(superView: self.view, color: .blue)
bottomView.topToBottom(of: centerView)
bottomView.centerX(to: centerView)
```
<img src="https://i.gyazo.com/47d0ee7c840a85d3334bc514181f7167.png" width=50%>


#応用
これまでの制約の付け方を応用して以下のようなレイアウトを作ります

<img src="https://i.gyazo.com/55e6fcb755e039022f83a23ba258b8cc.png" width=50%>

口でいうと
・真ん中に画面の横幅の半分の正方形ブロックを３つ並べる
・左側の余白に３つ目のブロックの上辺までの高さのバーを置く
・右側の余白に３つ目のブロックの底辺までの高さのバーを二つ置き、ひとつは幅が余白の0.3倍
・下側の余白にブロックを置く


```swift
    self.view.backgroundColor = .green
    
    let baseView = UIView()
    self.view.addSubview(baseView)
    baseView.edgesToSuperview()
    baseView.backgroundColor = .white
    
    let block1 = UIView()
    baseView.addSubview(block1)
    block1.width(UIScreen.main.bounds.width/2)
    block1.height(to: block1, block1.widthAnchor)
    block1.centerXToSuperview()
    block1.topToSuperview()
    block1.backgroundColor = .red
    
    let block2 = UIView()
    baseView.addSubview(block2)
    block2.size(to: block1)
    block2.centerX(to: block1)
    block2.topToBottom(of: block1)
    block2.backgroundColor = .blue

    let block3 = UIView()
    baseView.addSubview(block3)
    block3.size(to: block1)
    block3.centerX(to: block1)
    block3.topToBottom(of: block2)
    block3.backgroundColor = .black

    let leftView = UIView()
    baseView.addSubview(leftView)
    leftView.leftToSuperview()
    leftView.rightToLeft(of: block1)
    leftView.topToSuperview()
    leftView.bottom(to: block2)
    leftView.backgroundColor = .gray
    
    let rightBaseView = UIView()
    baseView.addSubview(rightBaseView)
    rightBaseView.leftToRight(of: block1)
    rightBaseView.rightToSuperview()
    rightBaseView.topToSuperview()
    rightBaseView.bottom(to: block3)
    
    let rightView1 = UIView()
    rightBaseView.addSubview(rightView1)
    rightView1.widthToSuperview(multiplier:0.3)
    rightView1.heightToSuperview()
    rightView1.leftToSuperview()
    rightView1.topToSuperview()
    rightView1.backgroundColor = .gray

    let rightView2 = UIView()
    rightBaseView.addSubview(rightView2)
    rightView2.heightToSuperview()
    rightView2.leftToRight(of: rightView1)
    rightView2.rightToSuperview()
    rightView2.topToSuperview()
    rightView2.backgroundColor = .blue
    
    let bottomView = UIView()
    baseView.addSubview(bottomView)
    bottomView.leftToSuperview()
    bottomView.rightToSuperview()
    bottomView.topToBottom(of: block3)
    bottomView.bottomToSuperview()
    bottomView.backgroundColor = .brown
```

safeAreaを考慮するとこんな感じです

<img src="https://i.gyazo.com/51cbcf4ab620360ba7a698a78b822f8c.png" width=50%>

コード的には最初の`baseView`のフラグをtrueにするだけ

```swift
    //これまでは同じ
    baseView.edgesToSuperview(usingSafeArea: true)
    //あとも同じ
```

#終わり
本当はアニメーションとかUIScrollViewとかUICollectionViewの制約とか実用的なとこまで書きたかったけど力尽きました
いつまで経ってもAutoLayoutの苦手意識が消えない自分がようやくTinyConstraintsのおかげでで実務でも使えるようになったので同じような苦手意識の人の助けになればなと思いまとめました
自分のメモのためにそのうち２も書きます