【Swift】超便利StackViewの実践的使い方！まだAutoLayoutで消耗してるの？


# まだAutoLayoutで消耗しているの？

AutoLayout...。それは今までエンジニアの寿命を縮めてきたものです。こんな経験ありませんか？

Twitterのようなタイムラインを作ろう!!
以下のようなタイムラインを考えてみます。



![スクリーンショット 2016-03-21 18.46.12.png](https://qiita-image-store.s3.amazonaws.com/0/48036/26672bcb-b2a8-a7af-4ea5-42a5a5038fc7.png "スクリーンショット 2016-03-21 18.46.12.png")

ツイートには画像があるものと無いものがあります。つまり、異なるレイアウトのセルを２つ用意して、場合分けして表示させる方法が一般的でしょう。どちらのセルにもAutoLayoutを設定しなければなりません。ツイートテキストのラベルの高さを可変になになるように設定したり、大変ですよね。

これだけではありません。タイムラインの仕様変更がありました。
『そうだ！リツイートや、ファボができるようにボタンを配置しよう！』
『...』
『AutoLayout付け直し...?オワタ』




![スクリーンショット 2016-03-21 18.53.40.png](https://qiita-image-store.s3.amazonaws.com/0/48036/e479aab4-607d-2a60-f9d1-d3f39571bdd4.png "スクリーンショット 2016-03-21 18.53.40.png")



このようにAutoLayoutは今まで僕らの寿命を縮めてきました。


# 救世主StackViewの登場

ここでは、上で出てきたタイムラインをStackViewを使ってレイアウトの設定をしていきます。
StackViewの特徴は以下のようなものです。

- AutoLayoutの設定が最小限に抑えられる
- hiddenプロパティに対応
- 部品を再配置がし易い(編集しやすい)
- 慣れれば超簡単
- イライラしない
- ステキ
- 好きになっちゃう

# 実践あるのみ。StackViewを使ってタイムラインを作ってみる

セルのレイアウトはxibファイルを使用して作っていきます。

# 普通に部品を配置する

まずは必要な部品を適当に配置します。(分かりやすく背景色をつけています)


![スクリーンショット 2016-03-21 19.05.53.png](https://qiita-image-store.s3.amazonaws.com/0/48036/dcff3b56-4423-a882-5e2a-8476903c51b2.png "スクリーンショット 2016-03-21 19.05.53.png")

ツイート画像を表示するためのImageViewもここで配置しておきます。

# StackViewを使う

StackViewを使用する時のポイントは以下の点です。

- StackViewは部品を暖かく包み込むように使用する
- 垂直方向、水平方向に並んでいる部品をそれぞれ包み込む


## 部品同士を暖かく包み込む


まずは小さなグループを見つけます。nameLabelとtimeLabelは水平方向に並んでいるのでStackViewで包み込んであげます。


![スクリーンショット 2016-03-21 19.16.30.png](https://qiita-image-store.s3.amazonaws.com/0/48036/c0ccfc7b-9234-4257-802f-ba09c913a66f.png "スクリーンショット 2016-03-21 19.16.30.png")




すると小さなStackViewの中にnameLabelとtimeLabelが包み込まれた状態になります。このStackViewをSmallStackViewと呼びましょう。

次に少し大きなグループを見つけます。
SmallStackView, textLabel, tweetImageViewがそれぞれ垂直方向に並んでいるのでStackViewで包み込んであげます。


![スクリーンショット 2016-03-21 19.22.51.png](https://qiita-image-store.s3.amazonaws.com/0/48036/cd08a12e-342d-e526-e75c-6d7cacf66717.png "スクリーンショット 2016-03-21 19.22.51.png")

ここで作成したStackViewをMediumStackViewと呼びましょう。
最後にMediumStackViewとiconImageViewが水平方向に並んでいるので、StackViewで包み込んであげます。




![スクリーンショット 2016-03-21 19.25.26.png](https://qiita-image-store.s3.amazonaws.com/0/48036/3d33a60a-53f5-9344-4ad6-39994aaa45c4.png "スクリーンショット 2016-03-21 19.25.26.png")

ここで作成されたStackViewをLargeStackViewと呼びましょう。

StackViewを設定すると部品が少し変な配置になってしまいますが、今はこのままでOKです。

### ViewControllerのソースコード

```ViewController.swift

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("TweetCell", forIndexPath: indexPath) as! TweetCell
        let tweet = self.tweets[indexPath.row]
        cell.iconImageView.image = tweet.icon
        cell.nameLabel.text = tweet.name
        cell.timeLabel.text = tweet.time
        cell.tweetLabel.text = tweet.text
        cell.tweetImageView.hidden = true
        if let image = tweet.tweetImage {
            cell.tweetImageView.hidden = false
            cell.tweetImageView.image = image
        }
        return cell
    }


```

このようにツイート画像がある時と無い時でhiddenプロパティを切り替えます。
hiddenにした部品はStackView内では存在しない事として扱われます。つまり、画像が含まれないツイートはImageViewが存在しないように振る舞ってくれます。
これによってセルを２つ用意する必要が無くなります。便利便利便利。

## 最低限のAutoLayoutを設定しましょう

LargeStackViewの大きさをセルの大きさと一致させるためにLargeStackViewにAutoLayoutを設定します。

制約は上下左右の余白をゼロとして設定します。
ここまでできたら一旦Runしてシミュレータで確認してみます。



![スクリーンショット 2016-03-21 19.31.17.png](https://qiita-image-store.s3.amazonaws.com/0/48036/44af6e62-4f09-1f5d-d98f-b65e7ffd65dd.png "スクリーンショット 2016-03-21 19.31.17.png")



見るも無残なモンスターが生まれてしまいました。

## 部分的にAutoLayoutを設定する

アイコンがとっても大きいのでAutoLayoutを設定しましょう。
アイコンに対してwidthとheightの値を具体的に設定し制約を追加します。


![スクリーンショット 2016-03-21 19.32.50.png](https://qiita-image-store.s3.amazonaws.com/0/48036/ccf87eda-129e-c6a8-69a6-211bc746c701.png "スクリーンショット 2016-03-21 19.32.50.png")


ちょっと綺麗になりました。
次にツイート画像の高さが大きすぎるので、ツイート画像のImageViewにもAutoLayoutを設定します。


![スクリーンショット 2016-03-21 19.36.16.png](https://qiita-image-store.s3.amazonaws.com/0/48036/7f69f533-5ea8-b8e0-0ea4-0372e8c154c4.png "スクリーンショット 2016-03-21 19.36.16.png")



だいぶ良くなってきました。
しかし、名前、時間、テキストのラベルが不格好なので整頓していきましょう。


## StackView内の部品の整列させる

MediumStackViewの中の部品をそれぞれ整頓させていきたいです。
そこで、StackViewのAlignmentを設定します。
AlignmentをFillに設定しましょう。

![スクリーンショット 2016-03-21 19.39.25.png](https://qiita-image-store.s3.amazonaws.com/0/48036/dc532d5b-6d42-c29e-3940-c7c1b19f752b.png "スクリーンショット 2016-03-21 19.39.25.png")

めちゃ良い感じになってきました。
しかし、このままですと部品同士がくっついていて窮屈です。そこで余白を設定してみましょう。

## 部品同士に余白を設定する

余白を設定するためにはStackViewのSpacingを設定します。
以下の画像のようにStackViewにSpacingを設定すると、StackViewに包まれている部品に余白を付けることができます。

![スクリーンショット 2016-03-21 19.45.47.png](https://qiita-image-store.s3.amazonaws.com/0/48036/60432fd1-ec96-dbcf-009e-8be06dd5308e.png "スクリーンショット 2016-03-21 19.45.47.png")

これで、とってもスッキリして気持よくなりました。
これで終わりでも良いのですが、この状態から新たに部品を配置してみたいと思います。


# 部品を追加する

ここでは、セルの下部にリプライ、リツイート、ファボの３つのボタンを配置します。


## 既存のStackViewに新たに部品を配置する。

MediumStackViewはSmallStackView, textLabel, tweetImageViewを垂直方向に配置させているものでした。このMediumStackView内に新たにButtonを配置します。




![スクリーンショット 2016-03-21 19.55.43.png](https://qiita-image-store.s3.amazonaws.com/0/48036/be47da4b-ec06-fad5-5ffa-7bd2eb72a4b4.png "スクリーンショット 2016-03-21 19.55.43.png")

次に、配置したButtonから水平方向に続くようにもう２つButtonを配置したい所です。
２つ目のButtonを配置する前に１つ目のButtonをStackViewで包んであげます。
この時StackViewのAxis属性を**Horizontal**に設定しましょう。ボタンを水平方向に配置していきたいからです。


![スクリーンショット 2016-03-21 20.01.46.png](https://qiita-image-store.s3.amazonaws.com/0/48036/38b61090-130f-7d14-ab2d-85258e621470.png "スクリーンショット 2016-03-21 20.01.46.png")

ここで作成したStackViewの中に残り２つのボタンを配置します。





![スクリーンショット 2016-03-21 20.04.30.png](https://qiita-image-store.s3.amazonaws.com/0/48036/7315b3ea-3e16-ed14-f783-509fc2fbcbde.png "スクリーンショット 2016-03-21 20.04.30.png")

上の画像のようにDistributionをFill Equallyに設定します。すると３つの部品のWidthが均等になるように配置してくれます。超便利です。

完成です。



![スクリーンショット 2016-03-21 20.51.04.png](https://qiita-image-store.s3.amazonaws.com/0/48036/ea416939-ec0b-83d0-36d9-72eac18a5dc0.png "スクリーンショット 2016-03-21 20.51.04.png")


ちなみに、画面を横に回転させても自動でレイアウトし直してくれます。とっても助かります。

![スクリーンショット 2016-03-21 21.42.39.png](https://qiita-image-store.s3.amazonaws.com/0/48036/f164adab-7144-378b-5ef9-b9ddd254e0a9.png "スクリーンショット 2016-03-21 21.42.39.png")



# 最後に

最後にポイントをおさらいです。

- StackViewは水平方向、垂直方向に並んでいる部品同士を暖かく包み込むように使用する
- ``hidden = true``を設定した部品は、その部品が存在しないようにレイアウトされる
- StackViewの大きさは中の部品の大きさ(テキストの量や画像サイズ)に依存する
- 特定の部品の大きさを制御したい場合は部分的にAutoLayoutを設定する。
- SpacingによってStackView内の部品同士の余白を設定する
- StackViewのアトリビュートインスペクタをイジイジすることで様々なパターンの整列を行うことができる(幅均等など)
- 画面回転時に自動でレイアウトし直してくれる。

[アプリのソースコードはコチラ](https://github.com/keitin/StackViewPractice)