UIStackViewのトルツメで警告が出るのを防ぐ方法


#UIStackView?強いよね
![hqdefault.jpg](https://qiita-image-store.s3.amazonaws.com/0/152335/f985cb4c-e40b-c856-037f-2755e80bbf47.jpeg "hqdefault.jpg")

均等間隔の配置、簡単な設定、制約の少なさ。隙がないと思うよ。

だけど・・・・時に警告が出るよ。

え〜。UIVieっ・・・UIViewたちが躍動するおいらのレイアウトを、皆さんに見せたいね。

#UIStackViewの落とし穴
そんな便利なUIStackViewですが、トルツメをしようと思った時に、思い通りの動きはするけど、なんか警告が出る・・・って時があります。

今回はその警告を消すための正しい制約の付け方について書きます。

#おさらい
UIStackViewを使わずに以下の画像のようにUIViewを配置した場合、赤のUIViewのisHiddenをtrueにしても青のUIViewを赤の位置に詰めることはできません。
![スクリーンショット 2017-12-25 22.02.47.png](https://qiita-image-store.s3.amazonaws.com/0/152335/8e810b2b-70f0-1c76-fa72-190a1ecc5884.png "スクリーンショット 2017-12-25 22.02.47.png")

なぜなら、赤のUIViewの高さの制約が存在し、青のUIViewは赤のUIViewの下に配置される制約があるため、赤のは見えなくなったとしても高さがあり、青のUIViewの位置が変わらないというわけです。

もしこの状態で赤を隠してトルツメをしたい場合は赤のUIViewの高さの制約を0にする必要があります。
![スクリーンショット 2017-12-25 22.10.28.png](https://qiita-image-store.s3.amazonaws.com/0/152335/d07b24db-bf94-e30b-481d-9c479bd1fe2c.png "スクリーンショット 2017-12-25 22.10.28.png")

![スクリーンショット 2017-12-25 22.10.52.png](https://qiita-image-store.s3.amazonaws.com/0/152335/6174b02d-a23c-3827-0103-9616bf33f3aa.png "スクリーンショット 2017-12-25 22.10.52.png")

コードで行う場合は、制約をIBOutletで接続して、高さの値を変更する必要があります。

```swift:ViewController.swift
class ViewController: UIViewController {

    @IBOutlet weak var redViewHeight: NSLayoutConstraint!
    
    private func changeRedViewHeight(_ isHidden:Bool) {
        if isHidden {
            redViewHeight.constant = 0
        } else {
            redViewHeight.constant = 300
        }
    }
}
```

例えば、以下のような場合だとどうでしょうか
![スクリーンショット 2017-12-25 22.15.33.png](https://qiita-image-store.s3.amazonaws.com/0/152335/9bdf63da-ec61-7541-79a5-bf7f904915ca.png "スクリーンショット 2017-12-25 22.15.33.png")

今回は、赤と青のUIViewの間に少し間を開ける制約を追加しました。
この場合だと赤の高さの制約に加えて、間を開ける制約もIBOutletを接続して0に設定しなければ赤の位置に青を持ってくることができません。
段々と複雑になればなるほど、コード上に制約のプロパティが散乱し始めたりするので、正直面倒ですね。

そんな時、UIStackViewはとても便利です。
まずは以下の画像のようにUIStackViewを作成してみましょう。
![スクリーンショット_2017-12-25_22_19_38.png](https://qiita-image-store.s3.amazonaws.com/0/152335/6603674e-941c-a059-938a-bacdecab6df3.png "スクリーンショット_2017-12-25_22_19_38.png")

するとこの画像のように二つのUIViewがUIStackViewに内包されます。
![スクリーンショット 2017-12-25 22.33.42.png](https://qiita-image-store.s3.amazonaws.com/0/152335/4024de4a-1a92-8179-e55f-1f3e6619322f.png "スクリーンショット 2017-12-25 22.33.42.png")

この状態だとUIStackViewになんの制約もついていないので、適当につけてあげましょう。
![スクリーンショット 2017-12-25 22.38.25.png](https://qiita-image-store.s3.amazonaws.com/0/152335/b9dc48b2-f30d-3e55-daad-c25b39509983.png "スクリーンショット 2017-12-25 22.38.25.png")

では、この状態で、赤のUIViewのhiddenを切り替えてみましょう。
![スクリーンショット 2017-12-25 22.46.06.png](https://qiita-image-store.s3.amazonaws.com/0/152335/86ea1b3f-3609-e6dc-d67a-4a2960406c78.png "スクリーンショット 2017-12-25 22.46.06.png")

赤のUIViewが非表示になり、青のUIViewが自動的に詰められています。
この方法を使用すると、IBOutletで制約をコード上に作ったりする必要もなく、Spacingで間を開けていても、それも自動で詰めることができます。

#しかし・・・
このままだと警告が出てしまいます。
![スクリーンショット 2017-12-25 22.48.21.png](https://qiita-image-store.s3.amazonaws.com/0/152335/7544cbf3-8b94-dda9-47e2-20170f3fe6ac.png "スクリーンショット 2017-12-25 22.48.21.png")

なぜ警告が出てしまうかというと、UIStackViewに内包されたUIViewはhiddenが切り替えられると、先ほどのコードと同じようにサイズや間隔の制約を0にするという仕組みで動いているので、先ほどのUIStackViewのTopとBottomの0の制約によって確定した高さと内包するUIViewの制約の高さの合計が変わってしまうことが原因です。

なので、以下のようにBottomの制約を削除することで警告が出なくなります。
![スクリーンショット 2017-12-25 23.33.25.png](https://qiita-image-store.s3.amazonaws.com/0/152335/ff39fd96-ab08-a0c7-0a2a-338e34185b15.png "スクリーンショット 2017-12-25 23.33.25.png")

UIStackViewは便利ですが、hiddenでトルツメをしたい場合にUIStackView自体のサイズが変わってしまうので、サイズを確定させるような制約を組むのではなく、あえて制約を決めなかったり、Priorityを変えてサイズの自由度を与えるのがいいのではないかと思います。

#余談
まさか自分がネタで作ったAdventCalendarが全部埋まるとは・・・
自分一人じゃ絶対無理でしたし、みなさんが書いてくれたおかげです！！！
ありがとうございました！

来年も抵抗するで？技術で🤜🤛