カスタムアニメーションによるView Controllerの遷移
本記事では、UIViewControllerAnimatedTransitioning
を用いてView Controllerをカスタムアニメーションで遷移させる方法を解説します。
トランジションのカスタマイズ方法
1. 遷移先のVIewControllerのサイズをカスタマイズする / 遷移時のアニメーションを簡易的にカスタマイズする→ こちら
2. 遷移時のアニメーションをカスタマイズする → 本記事
3. 遷移をインタラクティブにする → こちら
UIViewControllerAnimatedTransitioning
NSObjectに継承させるプロトコルで、View Controllerの遷移に対するカスタムアニメーションを実装することができます 。前回の記事で解説したUIPresentationControllerでは遷移中に利用するカスタムビューしかアニメーションを記述できませんが、UIViewControllerAnimatedTransitioning
を継承したアニメータを実装することで、遷移元・遷移先のView Controllerもアニメーションさせることができます。
このプロトコルを準拠するには、以下2つのメソッドの実装が必須です。
UIViewControllerContextTransitioningの注意点
これらのメソッドでは、引数としてUIViewControllerContextTransitioning
に準拠したオブジェクトが渡されます。これはView Controller間の遷移アニメーションに関する情報を提供するものですが、利用する際に2つの注意点があります。
まず1つ目は、isAnimated
をチェックすることです。この値がfalseだった場合にはアニメーションを生成すべきではありません。
2つ目の注意点は、アニメーションのcompletion handlerの中で必ずcompleteTransition(_:)
を呼ぶことです。これを呼び出すことで、UIKitにカスタムアニメーションの終了を知らせることができます 。
Presented / Dismissの判定
transitionContextにはpresent時のアニメーションなのかdismiss時のアニメーションなのかを判別するパラメータが存在しません。presentとdismissの区別が必要な場合には、カスタムアニメータにisPresented
パラメータを宣言し、animateTransition(using:)
で処理を分けます。
カスタムアニメータの指定
遷移先のView ControllerにUIViewControllerTransitioningDelegate
を継承し、animationController(forPresented:presenting:source:)
及びanimationController(forDismissed:)
メソッドの返り値として渡します。この時、カスタムアニメータのisPresented
を指定します。
サンプル
以下は、幅半分のView Controllerが右からスライドするように遷移するUIPresentationControllerおよびカスタムアニメータです。(UIPresentationControllerの実装は、前回の記事とほぼ同様のため省略します)
このサンプルでは遷移先のView Controller(サイドバー)が遷移元のView Controllerの上に重なるように表示されますが、animateTransition(using:)
の実装によっては遷移元のView Controllerがサイドバーの分だけ左にずれるような表示をさせることも可能です。
present時の遷移アニメーションについて、上のサンプルコードではtoView.frame.origin.x -= toView.bounds.width
と記述しています。toView
の初期位置は指定していませんが、アニメーション終了時(今回の場合はtoView.bounds.width
分だけ左に移動した時)にUIPresentationControllerのframeOfPresentedViewInContainerView
で指定した場所に存在するような座標に自動で設定されているように見えます。
修正 — 2020/04/14completeTransition(_:)
に対してUIView.animated
のcompleted
を渡していましたが、これはUIView.animated
が完了したかどうかを表すフラグであり、遷移が完了したかどうかはわかりません。正しくはtransitionContext.transitionWasCancelled
を用いて判定する必要があります。また、遷移がキャンセルされた場合には、アニメーションのために追加したview(上記のサンプルコードではtoView
)をsuperviewから取り除く必要があります。サンプルコードにおいて以上2点を修正しました。