LoginSignup
34
19

More than 3 years have passed since last update.

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

Last updated at Posted at 2019-02-15

世間話

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

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

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

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

UIStackView1.gif

// コードのイメージ
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

例えば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

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

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

ezgif-4-113a9e3ef94a.gif

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

別の条件でも試してみる

DistributionをFillEqualyにしてみる①

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

999ezgif-3-dc2421260fbe.gif

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

DistributionをFillEqualyにしてみる②

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

101010ezgif-3-278171c7c1da.gif

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

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

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

121212ezgif-3-8f15c3372e53.gif

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

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

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

131313ezgif-3-af1f5d76f701.gif

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

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

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

141414ezgif-3-80d352b25833.gif

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

UITableViewと連携する

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

444ezgif-3-786286a5c695.gif

何で?(殺意)

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

tableView.beginUpdates()
tableView.endUpdate()

こうか

UIView.animate(withDuration: 1.0) {
    tableView.beginUpdates()
    tableView.endUpdate()
}

こうです

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

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

self sizingを諦める

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

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

555ezgif-3-ad01298fcf3c.gif

できました。

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

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

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

34
19
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
34
19