Autolayout tips for iOS

## 曖昧な制約の確認・テスト
`Autolayout`の制約が不十分な場合、`hasAmbiguousLayout`を使用することで確認することができます。曖昧な制約が設定されてる場合は、`true`が返ります(制約が完全に決定してない場合は常に`false`が返ることに注意)。

[hasAmbiguousLayout](https://developer.apple.com/documentation/uikit/uiview/1622517-hasambiguouslayout)


曖昧な制約を持ったUIViewクラスは、`exerciseAmbiguityInLayout()`を実行することで、制約を満たす範囲で制約を変更してくれます。ただ、これはランダムに制約を変更されるので、デバックなどで使用するくらいがいいと思います。

[exerciseAmbiguityInLayout()
](https://developer.apple.com/documentation/uikit/uiview/1622545-exerciseambiguityinlayout)

`subviews`に対して`hasAmbiguousLayout`を使用することで`UIView`クラスや`UIVIewController`クラス単位で簡単なテストをすることも可能になります。

```swift:UIViewExtension 
extension UIView {
    var isAmbiguousLayout: Bool {
        var isAmbiguous = false
        let views = [self] + subviews
        views.forEach { view in
            if view.hasAmbiguousLayout {
                isAmbiguous = true
                print(view.description)
            }
        }
        return isAmbiguous
    }
}
```

```swift
func testAutolayout() {
    let viewController = SampleViewController.make()
    XCTAssertFalse(viewController.view.isAmbiguousLayout)   
}
```

## 制約の出力

### コンフリクトした制約
コンフリクトした制約はコンソールにて確認できますが、`Identifier`を使用することで即座にコンフリクトした制約を確認することができます。`storyboard`(`xib`)上でidentifierを設定する方法とコードで設定する二通りの方法があります。


```swift
@IBOutlet weak var constraint: NSLayoutConstraint!
...
constraint.identifier = "constraint1"
```

<img width="258" alt="スクリーンショット 2017-07-24 0.36.00.png" src="https://qiita-image-store.s3.amazonaws.com/0/62451/adbcb05d-78c4-d17f-cc37-faa0c852c7d1.png">


実際に次のような制約のコンフリクトが発生している場合、コンソールには次のエラーログが出力されます。

<img width="388" alt="スクリーンショット 2017-07-24 0.35.11.png" src="https://qiita-image-store.s3.amazonaws.com/0/62451/ddae2a00-b00a-26f9-529e-fb03b747ba79.png">

```
Probably at least one of the constraints in the following list is one you don't want. 
	Try this: 
		(1) look at each constraint and try to figure out which you don't expect; 
		(2) find the code that added the unwanted constraint or constraints and fix it. 
(
    "<NSLayoutConstraint:0x6000000846a0 'topConstant1' V:[_UILayoutGuide:0x7f8c07f05f40]-(50)-[UILabel:0x7f8c07c0b4a0'Label']   (active)>",
    "<NSLayoutConstraint:0x600000084920 'topConstant2' V:[_UILayoutGuide:0x7f8c07f05f40]-(100)-[UILabel:0x7f8c07c0b4a0'Label']   (active)>"
)
```

実際にコンフリクトしている制約は、かっこの中の部分です。`<クラス名：メモリアドレス名 'identifier' レイアウト概要>`が出力されるようになっています。通常の画面では複数のコンポーネントがあることが多いので、積極的に`identifier`をつけるのがいいかなと思っています。

## 制約の取得

UIViewクラスがもつ[constraints](https://developer.apple.com/documentation/uikit/uiview/1622464-constraints)でviewがもつ制約が配列で取得可能ですが、[constraintsAffectingLayout(for:)](https://developer.apple.com/documentation/uikit/uiview/1622432-constraintsaffectinglayout)では、水平方向、垂直方向の制約を指定して取得することができます。


## ブレイクポイントの設定

また、制約のエラーは`UIViewAlertForUnsatisfiableConstraints`のシンボリックブレイクポイントによって捉えることができます。
[Autolayout Breakpoints
](http://nshint.io/blog/2015/08/17/autolayout-breakpoints/)に詳しい記述があります。デバッカーコマンドの`_autolayoutTrace`というメソッドは`Objective-C`のプライベートメソッドになるので、ブリッジを定義すればコード上からも実行することが可能です。

## ビューデバッカーから制約を確認する

ビューデバッカー上で制約を確認することも可能です。実際にデバックしている制約を確認できるので頻繁に利用しています。

方法はビューデバッカーモードの状態から、画像の青いボタンの`Show Constraint`ボタンを選択し、対象のオブジェクトを選択するだけです。
<img width="150" alt="スクリーンショット 2017-07-24 1.14.45.png" src="https://qiita-image-store.s3.amazonaws.com/0/62451/1b13adff-27b2-d8b7-7437-63ab9a953bdb.png">

実際に表示された画面が次のようになります。

<img width="565" alt="スクリーンショット 2017-07-24 1.15.27.png" src="https://qiita-image-store.s3.amazonaws.com/0/62451/642b5178-d1d9-5414-d41d-34aae7074d6a.png">

制約を選択すると制約の詳しい情報が見れるので、実際に表示されてる制約が正しいかここで確認するようにしています。

<img width="262" alt="スクリーンショット 2017-07-24 1.15.40.png" src="https://qiita-image-store.s3.amazonaws.com/0/62451/1b09c7c7-5cf2-3d7c-b080-06825b800eb1.png">