【Swift5.x】MainTabBarを切り替えた時にシュッとアニメーションさせる
はじめに
タイトル通り。MainTabBarを切り替えた時にシュッとアニメーションさせると見栄えするし、アプリ側でタブ切り替えを行う時にユーザーにそれが伝わりやすい。ぶっちゃけNetflixのパクリ
ソースコード
ビルド結果
@tos pic.twitter.com/Jbzyux09qU
— ゆっちゃん/ユウ (@yuhachi0220) February 7, 2020
上のソースコードをビルドするとこんな感じ。ビューのデザインとかは新しいプロジェクトを作ったときのデフォルトのやつです(作るのがめんどくさかった)。
@tos 2 pic.twitter.com/2wkMFOoqi3
— ゆっちゃん/ユウ (@yuhachi0220) February 7, 2020
1/10倍速。動画をスロー再生してるわけじゃなくて、animateメソッドのwithDurationを10倍にしてビルドしたやつを録画してます。
中身の話
正直ソースコードを貼り付けた時点で書くことがなにもないんですが、一応色々書いていこうと思います。
delegateメソッド
UITabBarControllerDelegateのtabBarController(_:animationControllerForTransitionFrom:to:)メソッドを使います。
func tabBarController(_ tabBarController: UITabBarController, animationControllerForTransitionFrom fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { guard let viewControllers = tabBarController.viewControllers, let fromIndex = viewControllers.firstIndex(of: fromVC), let toIndex = viewControllers.firstIndex(of: toVC) else { return nil } let scrollRight = toIndex > fromIndex return TabBarAnimatedTransitioning(scrollRight) }
このメソッドの返り値に指定したUIViewControllerAnimatedTransitioningオブジェクトのanimateTransitionメソッドを使っていい感じにアニメーションしてくれます。fromVC、toVCが遷移元のviewControllerと遷移先のviewControllerになるので、これを使ってアニメーションを作ります。
このメソッドを使わなくても、タブバーのタップを検出するメソッドは他にもあるので、自前のメソッドでアニメーションを再生することもできるんですが、特別な理由がない限り既存のSDKでできる事を再開発する必要もないと思います。
第3のビューがアニメーションに登場するとかならハックする必要があるかもしれません(そんなシチュエーションあるか?)。
横向きにスクロールするということで、scrollRightくんを生成します。彼がtrueなら右スクロール、falseなら左スクロールという感じですね。この値はviewControllersのindexを使って決めます。
余談ですが、最近のiOSではarray.index(:)ではなくarray.firstIndex(:)を使うようになっています。まぁビルドターゲットのバージョンに合わせてXcodeくんが添削してくれるので、あんまり意識することはないと思いますが。
あんまりないとは思いますが、上下とかにもスクロールするシチュエーションがある場合、enumを使ったほうがよさそうです。左右ならわざわざenumを書くほどでもない気がする(書いたほうがわかりやすい気もする)。
transitionアニメーションの生成
UITabBarControllerDelegateのtabBarController(_:animationControllerForTransitionFrom:to:)メソッドの返り値にTabBarAnimatedTransitioning(_: Bool)メソッドを初期化して渡しています。
このメソッドの名前は何でもいいので、プロジェクトに合わせてかっこいいネーミングをしていくといい感じになります。
NSObjectとUIViewControllerAnimatedTransitioningを継承させます。NSObjectProtocolに準拠しているプロトコルなので、NSObjectを継承していないとまぁまぁめんどくさい感じになります(色々stubしろよって言われる)。
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { guard let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from), let toView = transitionContext.view(forKey: UITransitionContextViewKey.to) else { return } transitionContext.containerView.addSubview(toView) let screenWidth = UIScreen.main.bounds.size.width / 4 let offset = (scrollRight ? screenWidth : -screenWidth) toView.alpha = 0 toView.center.x += offset UIView.animate(withDuration: transitionDuration(using: transitionContext), delay: 0.0, usingSpringWithDamping: 1, initialSpringVelocity: 0, options: [.curveEaseInOut], animations: { fromView.alpha = 0 fromView.center.x = fromView.center.x - offset toView.alpha = 1 toView.center.x = toView.center.x - offset }, completion: { transitionContext.completeTransition($0) }) }
UIViewControllerAnimatedTransitioning - UIKit _ Apple Developer Documentation
animateTransitionメソッドの中でanimateメソッドを使ってアニメーションさせます。
animateTransition(using_) - UIViewControllerAnimatedTransitioning _ Apple Developer Documentation
UIKitはViewControllerを表示または非表示する際にこのメソッドを呼び出す、とあるので、手作業でどこでメソッドを実行して〜みたいなコードを書く必要はないです。
UIScreenの横幅を取得して1/4にしたものをoffsetとして、遷移先ビューの初期値をoffsetぶん横にずらし、alphaを0にセットします。あとはanimateメソッドの中で遷移元ビューのalphaを0にしつつoffsetぶん横にずらしながら、同時に遷移元ビューのalphaを1にしつつ中央に戻します。
アニメーションの再生が終わったらcompletionが呼ばれるので、transitionContextにトランジションが終わったことを通知します。fromViewのremoveFromSuperView()なんかは書かなくても通知した時に勝手にやってくれるみたいです。
animateメソッドを使わない場合、UIKitはトランジションが終了した際にanimationEnded(_:)メソッドを呼び出すらしいので、こちらでtransitionContextへの通知を行う感じになるでしょうか。たぶんなると思います(試してない)。
animationEnded(__) - UIViewControllerAnimatedTransitioning _ Apple Developer Documentation
MainTabBarControllerでの処理
ビューのalphaをいじってビューが消えているっぽく見せているので、viewDidLoad()でビューのbackgroundColorをセットしておきます。
override func viewDidLoad() { super.viewDidLoad() self.delegate = self view.backgroundColor = UIColor.systemBackground }
iOS13か12あたりから.systemBackgroundが使えます。OSの設定がダークモードなら黒、ライトモードなら白になります。
それ未満のバージョンに対応する場合は@availableを使って分岐させるのが手っ取り早いと思います。.systemBackgroundを使う時に、プロジェクトのビルドターゲットに応じてXcodeがエラーを吐いてくれます。たぶん。
アプリのイメージカラーにもよりますが、だいたいは白か黒にしておけば間違いないと思います。デザイナーさんと相談しましょう。
参考ページ
記事中でも紹介したAppleのドキュメントたち
UIViewControllerAnimatedTransitioning - UIKit _ Apple Developer Documentation
animateTransition(using_) - UIViewControllerAnimatedTransitioning _ Apple Developer Documentation
animationEnded(__) - UIViewControllerAnimatedTransitioning _ Apple Developer Documentation
それ以外の参考にしたページ
Making Tab Bar Slide When Selected _ @samwize
iOS Dev Course_ UITabBarController Animated Transitioning
終わりに
やばいな。もう書くことがない。
最初の頃は結構迷走したりしてたんですが、つよいエンジニアに色々教えてもらってわりといい感じにまとまったんじゃないかと思います。
ざっくりしたことしか書いてないですが、何かしらの参考になると嬉しいです。
変なこと書いてるぞボケェ!って思ったらTwitterで教えて下さい。コメントは見ないわけじゃないけどあんまり見ないので反応が遅れます。