Transition(遷移アニメーション)は、あるコンテンツを別のコンテンツに視覚的に切り替える仕組みです:画面、フラグメント、ビュー、インターフェースの状態。適切に実装されたトランジションはユーザーの注意を誘導し、要素間の関係を説明し、ナビゲーションを直感的にします。Material Design Guidelines(2026)によると、適切に設計されたトランジションを持つアプリは、2回目のセッションで22%高いユーザー維持率を示します。他の種類のアニメーションについては、アニメーションの一般ガイドをご覧ください。
重要なポイント
Transitionは、ある視覚的インターフェース状態を別の状態に置き換える際に発生するアニメーションです。Property Animation(単一オブジェクトのプロパティをアニメーション化)とは異なり、Transitionは一連の変更全体を管理します:要素の出現/消失、位置、サイズ、プロパティの変更を1つのトランジション内で行います。Transitionは、ユーザーが何がどこに移動したかを理解できるように、コンテンツのスムーズな変更を解決します。
モバイルプラットフォームでは、Transitionは3つの主要なシナリオで使用されます:画面間のナビゲーション(push、pop、modal present)、フラグメントの変更(Android FragmentTransaction)、画面内の状態変更(カード展開、タブ切り替え)。各シナリオには独自のアプローチが必要です:ナビゲーショントランジション — iOSではUIViewControllerAnimatedTransitioning、AndroidではActivityOptions、画面内トランジション — Transition FrameworkまたはUIView.transition。
Apple HIG(2026)によると、標準的なトランジションの持続時間は400〜500 msを超えてはなりません — より長いアニメーションは遅延として認識されます。Material Designはモバイルトランジションに300〜350 msを推奨しています。大きな要素(カード、画像)の場合、知覚パフォーマンスを損なうことなく、時間を500〜600 msまで延ばせます。
トランジションの種類は、どのコンテンツがどのように変更されるかに応じていくつかのカテゴリに分類されます。基本タイプ:cross-fade — 一方がスムーズに消え、もう一方が現れる;slide — 一方の画面が出ていき、もう一方が入ってくる;scale/zoom — 要素が拡大して詳細画面を開く。複合トランジションは複数の効果を組み合わせます。
| トランジションの種類 | 説明 | iOS API | Android API |
|---|---|---|---|
| Cross-fade | フェード:一方のコンテンツが消え、もう一方が現れる | UIView.transition(with:duration:options:.transitionCrossDissolve) | TransitionSet + Fade() |
| Slide | スライド:コンテンツが水平または垂直に出入りする | CATransition(type: .push, subtype: .fromLeft) | Slide() / FragmentTransaction.setCustomAnimations |
| Scale / Zoom | スケーリング:要素が全画面に拡大する | UIViewPropertyAnimator(スケールアニメーション) | Scale() + ActivityOptions.makeScaleUpAnimation |
| Explode | 爆発:コンテンツが消える際に断片化する | CATransition(type: .reveal) | Explode()(API 21+) |
| Fade-through | 白/黒画面を通したフェード | UIView.transition(with: .transitionCrossDissolve、白背景) | ChangeBounds経由のFade() |
Material Designのルール:統一された視覚言語を作成するために、1つのアプリ内で1つのトランジションタイプを使用します。推奨:同じレベルの画面間のナビゲーションにはslide(マスター→詳細)、1つの画面内のコンテンツ変更にはcross-fade(タブ、ビュー切り替え)、カードから詳細画面への遷移にはscale/zoom(商品カード→説明)。
iOSでは、UIView.transitionが単一のView内でのコンテンツ変更をアニメーション化する最も簡単な方法です。cross-dissolve、curl-up、curl-down、flip-from-left、flip-from-rightをサポートしています。UIViewController間のナビゲーショントランジションには、UIViewControllerAnimatedTransitioningと共にUINavigationControllerDelegateを使用します — これによりpush/popアニメーションを完全に制御できます。
import UIKit
// UIView.transition: cross-dissolveで画像を切り替え
UIView.transition(with: imageView,
duration: 0.4,
options: [.transitionCrossDissolve, .curveEaseInOut]
) {
self.imageView.image = UIImage(named: "new_image")
} completion: { _ in
print("Transition completed")
}
// UIView.transition: 2つのビューを切り替え
UIView.transition(from: firstView, to: secondView,
duration: 0.5,
options: [.transitionFlipFromRight, .showHideTransitionViews]
) { _ in
// firstViewが非表示、secondViewが表示
}
// CATransition(Auto Layoutなし)
let transition = CATransition()
transition.type = .push
transition.subtype = .fromRight
transition.duration = 0.35
transition.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
view.layer.add(transition, forKey: "pushTransition")
UIViewPropertyAnimatorは、iOS 10+向けのカスタムトランジションの最新の代替手段です。UIView.transitionとは異なり、PropertyAnimatorは一時停止、キャンセル、リバースアニメーションをサポートしており、インタラクティブトランジション(スワイプで戻る)に重要です。UIViewController間のアニメーションには、PropertyAnimatorと共にUIPercentDrivenInteractiveTransitionを使用します — これがインタラクティブナビゲーションの標準パターンです。
Androidでは、FragmentTransactionとsetCustomAnimationsがFragment間のトランジションをアニメーション化する主要な方法です。パラメータ:enter(新しいFragmentの入場アニメーション)、exit(古いFragmentの退場アニメーション)、popEnterとpopExit — 戻るナビゲーション用。FragmentTransactionはアニメーションリソース(R.anim)とTransition Framework(R.transition)をサポートしています。
import androidx.fragment.app.FragmentTransaction
import androidx.transition.TransitionInflater
// FragmentTransactionとリソースアニメーション
supportFragmentManager.beginTransaction()
.setCustomAnimations(
R.anim.slide_in_right, // enter
R.anim.slide_out_left, // exit
R.anim.slide_in_left, // popEnter
R.anim.slide_out_right // popExit
)
.replace(R.id.container, DetailFragment())
.addToBackStack(null)
.commit()
// FragmentTransactionとTransition Framework
val detailFragment = DetailFragment()
detailFragment.enterTransition = TransitionInflater.from(this)
.inflateTransition(R.transition.slide_right)
detailFragment.exitTransition = TransitionInflater.from(this)
.inflateTransition(R.transition.fade_out)
supportFragmentManager.beginTransaction()
.replace(R.id.container, detailFragment)
.addToBackStack(null)
.commit()
// ActivityOptions: Activity起動時のアニメーション
val options = ActivityOptions.makeScaleUpAnimation(
view, 0, 0, view.width, view.height
).toBundle()
startActivity(intent, options)
Androidのベストプラクティス:Slide() — ナビゲーションでのFragment置き換え用(enter = Slide(Gravity.END)、exit = Slide(Gravity.START))。Fade() — オーバーレイとダイアログの表示/非表示用。Explode() — アクセントトランジション用(リストからアイテムを削除)。古いデバイス(API < 21)では、フォールバックとしてR.animリソースを使用します。Transition FrameworkはSurfaceView、TextureView、GLSurfaceViewをサポートしていません — ビデオとカメラにはR.animアニメーションのみを使用してください。
UIViewControllerAnimatedTransitioningは、完全にカスタムなビューコントローラ遷移アニメーションを作成するためのiOSプロトコルです。コンテナビュー(transitionContext.containerView)、ソースビュー、ターゲットビューへのアクセスを提供します。UIPercentDrivenInteractiveTransitionと組み合わせることで、このアプローチは指でのスクロールによるインタラクティブトランジションを作成できます。
import UIKit
"> カスタムトランジションアニメーター
class ScaleTransitionAnimator: NSObject, UIViewControllerAnimatedTransitioning {
let isPresenting: Bool
init(presenting: Bool) { self.isPresenting = presenting }
func transitionDuration(using ctx: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.4
}
func animateTransition(using ctx: UIViewControllerContextTransitioning) {
guard let toView = ctx.view(forKey: .to),
let fromView = ctx.view(forKey: .from)
else { return }
let container = ctx.containerView
let initialScale: CGAffineTransform = CGAffineTransform(scaleX: 0.8, y: 0.8)
let finalFrame = ctx.finalFrame(for: toView)
if isPresenting {
toView.transform = initialScale
toView.alpha = 0
toView.frame = finalFrame
container.addSubview(toView)
}
UIView.animate(withDuration: transitionDuration(using: ctx),
delay: 0,
usingSpringWithDamping: 0.7,
initialSpringVelocity: 0.3) {
if self.isPresenting {
toView.transform = .identity
toView.alpha = 1
fromView.transform = CGAffineTransform(scaleX: 0.9, y: 0.9)
fromView.alpha = 0
} else {
fromView.transform = initialScale
fromView.alpha = 0
}
} completion: { _ in
fromView.transform = .identity
ctx.completeTransition(!ctx.transitionWasCancelled)
}
}
}
// UINavigationControllerDelegateでの適用
extension MainViewController: UINavigationControllerDelegate {
func navigationController(
_ nc: UINavigationController,
animationControllerFor operation: UINavigationController.Operation,
from: UIViewController, to: UIViewController
) -> UIViewControllerAnimatedTransitioning? {
return ScaleTransitionAnimator(presenting: operation == .push)
}
}
インタラクティブトランジション:ソース画面にUIPanGestureRecognizerを追加します。ジェスチャーがトリガーされたら、UIPercentDrivenInteractiveTransitionを作成し、update(CGFloat(percent))を呼び出します。しきい値(通常30%)に達しない場合のキャンセルにはcancel()を呼び出します。このパターンは、UINavigationControllerのpopジェスチャーと、pagingEnabledを有効にしたUIScrollViewでデフォルトで使用されます。カスタムジェスチャーには、UIViewControllerInteractiveTransitioningを実装します。
Transition Framework(android.transition)は、2つのシーン(Scene)間の変更を自動的にアニメーション化する宣言型アニメーションシステムです。シーンは特定の状態を持つViewsのセットです。シーンAからシーンBに移行する際、Transition Frameworkは差分を分析してアニメーション化します:位置変更(ChangeBounds)、サイズ変更(ChangeClipBounds、ChangeTransform)、出現(Fade)と消失(Fade)。
import android.transition.Scene
import android.transition.TransitionManager
import android.transition.TransitionSet
import android.transition.AutoTransition
import android.transition.ChangeBounds
import android.transition.Fade
// TransitionSet: ChangeBounds + Fadeの組み合わせ
val transition = TransitionSet().apply {
ordering = TransitionSet.ORDERING_TOGETHER
addTransition(ChangeBounds())
addTransition(Fade(Fade.IN))
duration = 400L
interpolator = FastOutSlowInInterpolator()
}
// ViewGroup内のシーン間遷移
val sceneRoot = findViewById<ViewGroup>(R.id.scene_root)
val scene1 = Scene.getSceneForLayout(sceneRoot, R.layout.scene_initial)
val scene2 = Scene.getSceneForLayout(sceneRoot, R.layout.scene_expanded)
"> 手動設定なしのAutoTransition
TransitionManager.go(scene2, AutoTransition().apply {
duration = 350L
})
// TransitionManager.beginDelayedTransition — シーンなし
TransitionManager.beginDelayedTransition(sceneRoot, ChangeBounds())
itemView.layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT
itemView.requestLayout() "> layoutの変更は自動的にアニメーション化
ChangeBoundsはAndroidで最も使用されるトランジションで、Viewの位置とサイズの変更をアニメーション化します。ChangeTransformは回転とスケールをアニメーション化します。ChangeImageTransform — ImageViewアニメーション用(centerCrop → fitCenter)。階層構造の変更を伴う複雑なトランジションには、ordering = ORDERING_SEQUENTIALを指定したTransitionSetを使用します(アニメーションは並列ではなく順次実行されます)。Android Developers(2026)のドキュメントによると、Transition Frameworkはすべての標準Viewをサポートしていますが、サーフェスアニメーション(SurfaceView、TextureView、VideoView)はサポートしていません。
よくある質問
Animation — 単一オブジェクトのプロパティ(alpha、translation、scale)をアニメーション化します。Transition — オブジェクトまたは状態のセットの変更をアニメーション化します(fragment → fragment、scene → scene)。Transitionは複数の変更を同時に管理します:出現、消失、移動、サイズ変更 — そして2つの状態間の差分を自動的にアニメーション化します。Animationは各プロパティの手動管理が必要です。
Transition Frameworkと共にFragmentTransaction.setCustomAnimationsを使用します:fragment1.exitTransition = Fade(Fade.OUT)、fragment2.enterTransition = Slide(Gravity.END)。順序付けには:setReorderingAllowed(true) — フラグメントがスムーズな並列トランジションのために並べ替えられます。単純なケースでは、R.animのslide_in_right / slide_out_leftで十分です。戻るナビゲーションにはpopEnterとpopExitを指定します。
Transition Frameworkは、レイアウトの再計算をトリガーしないプロパティのみをアニメーション化します。高さのアニメーションには、ChangeBoundsを使用します — 2つのレイアウト状態間のView境界の変更を自動的にアニメーション化します。LayoutParamsを変更する前に、TransitionManager.beginDelayedTransition(viewGroup, ChangeBounds())を呼び出します。高さがWRAP_CONTENTで変更される場合、layout_heightが前後で異なることを確認してください — 両方の状態がmatch_parentの場合、ChangeBoundsは機能しません。
AnyTransitionと共に.transition()修飾子を使用します:.transition(.move(edge: .trailing).combined(with: .opacity))。カスタムトランジションには、AnyTransition.asymmetric(insertion: .move(edge: .trailing), removal: .move(edge: .leading))を実装します。NavigationStack内でのアニメーションには、iOS 18+で.navigationTransition(.slide)を使用します。SwiftUIはUIViewControllerAnimatedTransitioningをサポートしていません — AnyTransitionによる宣言型トランジションのみです。
Profile GPU Rendering(Android)またはCore Animation Instrument(iOS)を使用します。Androidでは、adb shell setprop debug.transition.profiling 1でTransition Profilingを有効にします — ログに各トランジションステージの持続時間が表示されます。iOSでは、シミュレーターでSlow Animationsを有効にします。目標指標:持続時間 < 400 ms、FPS >= 55、フレームドロップ — トランジションあたり2回以下。15以上の同時アニメーションViewを持つ複雑なシーンでは、300 msの持続時間でChangeBoundsを使用します。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。