[iOS]UIStackViewのアニメーションが変！

## 世間話

今更UIStackViewと戯れているんですが、ちょいちょいわけわからん動きをしますね。
UITableViewと格闘して大嫌いになった人達がUIStackViewを絶賛していましたが、彼らは大丈夫だったんでしょうか。こっちも結構曲者ですよ・・・

## Hide/Showアニメーションが変

UIStackViewの良いところは、なんと言ってもisHiddenでViewの表示／非表示が簡単にできるところですよね。
iOSでもAndroidのgoneが使いたいんだ！と何度思ったことが。

ですが、表示／非表示のAnimationをしようとしたところ、変な動きになりました。

![UIStackView1.gif](https://qiita-image-store.s3.amazonaws.com/0/20784/30e4b4aa-a471-a96d-179e-2c6299b550a8.gif)

```swift
// コードのイメージ
UIView.animate(withDuration: 0.3) {
    self.stackView.arrangedSubviews[index].isHidden.toggle()
    self.stackView.layoutIfNeeded()
}
```

一番上と一番下、お前らなんなん！？
ここらへん、ググっても全然出てきませんでした。

ちなみに条件は

- Alignment Fill
- Distribution Fill
- Spacing 0
- 各Viewにheightのconstraint


## ああ、え、そういうこと？

半透明にしたらわかりました。

![ezgif-3-d65353ce2ed2.gif](https://qiita-image-store.s3.amazonaws.com/0/20784/f2ac1568-03c1-9960-e75d-e0dd0474ba3e.gif)

例えば3番目のViewをhiddenにしようとすると、4番目以降のViewが上にせり上がってきて、4番目のViewが3番目のViewに達したあとで3番目のViewが見えなくなります。
何で？（殺意）
これで最下部のViewの動きに合点がいきます。
最上部のViewの動きは何かルールが違っている気がしますが。。

## 意図した動きにするにはどうすればいいか？

この動きは非常に厄介です。
例えばViewが2つのStackViewを作って、上部をタイトル情報、下部に詳細情報にして、タップしたら開くとか、そういう基本的な動きすらアニメーションができなくなります。


### そうだ、ClipToBoundsだ！

天啓を得てUIStackViewのClipToBoundsをtrueにしてみました。
何の成果も得られませんでした。
何で？（殺意）

### 解1：親Viewを作って、親ViewをClipToBoundsにする

上下左右をAutoLayoutでつないでやります。
これでいけました。

![ezgif-4-1e69df04900b.gif](https://qiita-image-store.s3.amazonaws.com/0/20784/ccd47c7f-dd51-d51f-6639-21ec3dffcf8a.gif)


### 解2：高さのconstraintのPriorityを999にする

ちょっと隠れ方の挙動が違いますがこれでもいけました。
何で？（殺意）

![ezgif-4-113a9e3ef94a.gif](https://qiita-image-store.s3.amazonaws.com/0/20784/73a476d4-7dd0-0fd2-1f9c-a144e821cea1.gif)

すいませんが、これまだ理解できていません。
1000ならダメで、999ならいけるんです。

## 別の条件でも試してみる

### DistributionをFillEqualyにしてみる①

UIStackView自体の高さを縛りました。

![999ezgif-3-dc2421260fbe.gif](https://qiita-image-store.s3.amazonaws.com/0/20784/e1275f2a-4500-99df-45f7-7134632a9345.gif)

予想通りですが若干キモいですね、使い所が限られそう？

### DistributionをFillEqualyにしてみる②

今度は一番上のViewの高さを縛ってみました

![101010ezgif-3-278171c7c1da.gif](https://qiita-image-store.s3.amazonaws.com/0/20784/25aa7ee6-9188-dd10-9fdb-e03044bce55b.gif)

良さそうな動きをしますが、基準となる一番上のViewをhiddenにしたところ壊れてしまいました。
どのように高さを設定するか考える必要が出てきそうです。
確実に表示するViewがあればいいんですが。

### Spacingがついていたらどうなる？①

Spacingをつけて、各Viewの高さを縛りました。

![121212ezgif-3-8f15c3372e53.gif](https://qiita-image-store.s3.amazonaws.com/0/20784/59912454-a2af-a2a9-27b1-efacb2ea2caf.gif)

ああっ！そうなっちゃいますよね。
これはいけません。

### Spacingがついていたらどうなる？②

UIStackViewの高さを縛ってみました。

![131313ezgif-3-af1f5d76f701.gif](https://qiita-image-store.s3.amazonaws.com/0/20784/c417aaa9-d739-bde7-64d4-75906b44560b.gif)

これはちょっと使えそうですね。

### Spacingがついていたらどうなる？③

動いてから消えるのが良くないので、消してから動かしてみましょう。
isHiddenは使えないので透明度をいじっています。

![141414ezgif-3-80d352b25833.gif](https://qiita-image-store.s3.amazonaws.com/0/20784/63e36cfb-95b1-434a-6198-6189f5c22723.gif)

手品っぽくなりました。これはギリギリよさそうです。

## UITableViewと連携する

やっぱり同じセルが増えてくるとTableViewに頼らざるを得なくなってきます。
UITableViewCellにUIStackViewを入れて、アニメーションしてみましょう。
AutoLayoutは上下左右をひっつけます。

![444ezgif-3-786286a5c695.gif](https://qiita-image-store.s3.amazonaws.com/0/20784/8f6c715c-0a38-5d3b-0570-eb98cb85f471.gif)

何で？（殺意）

ちなみにアニメーションは

```swift
tableView.beginUpdates()
tableView.endUpdate()
```
こうか

```swift
UIView.animate(withDuration: 1.0) {
    tableView.beginUpdates()
    tableView.endUpdate()
}
```
こうです

さっきは、動いてから消えましたよね。
何で今回は消えてから動くんですか？？？？

5分ぐらい考えてみましたがわかりません。保留です。

### self sizingを諦める

しょうがないので、UITableView.automaticDimensionを諦めてみます。

UIStackViewとCellを、上左右のみ設定して、UIStackViewの高さのコントロールは上でやったとおりにします。
そしてCellの開閉アニメーションはオーソドックスにheightを変更して行います。

![555ezgif-3-ad01298fcf3c.gif](https://qiita-image-store.s3.amazonaws.com/0/20784/fef1ea31-716a-4179-a126-43c72c5ce99f.gif)

できました。

しかしself sizing好きなので、これには私も憤慨です。
特に今の高さではなくて、1個のViewをhidden/showしたあとの高さを求めるのがだるくてしょうがないです。

一応作りましたけど。スマートではないですね。

```swift
func expectedHeight(when viewOfIndex:Int, isHidden:Bool) -> CGFloat {
    let height = arrangedSubviews.filter { !$0.isHidden }.reduce(0, { $0 + $1.frame.height })
    if isHidden && !arrangedSubviews[viewOfIndex].isHidden {
        return height - arrangedSubviews[viewOfIndex].frame.height
    }
    if !isHidden && arrangedSubviews[viewOfIndex].isHidden {
        return height + arrangedSubviews[viewOfIndex].frame.height
    }
    return height
}
```

## おわりに

内部でAutoLayoutがどうなってるのか見えにくいので、わからないことが多すぎます。
一個一個知識や経験として仲良くなっていくしか無い気がします。

闇が深い・・・

間違いや備考などあったら後ほど追加します。

## 次回

UIStackViewってちょいちょい言うこと聞かないですよね。
~~それで「私の本当に欲しかったStackView」を作ったのでその話をします。~~
→　何か使ってみるとイマイチパッとしなかったのでやめます

よりリッチなものは既にライブラリが存在します。すばらしいです、よく作りますねこんなの。
ただちょっとヘビー？

StackViewController
https://github.com/seedco/StackViewController
AloeStackView
https://github.com/airbnb/AloeStackView