Transition (анімація переходу) — механізм візуальної зміни одного контенту іншим: екранів, фрагментів, в'ю або станів інтерфейсу. Правильно реалізований перехід спрямовує увагу користувача, пояснює взаємозв'язок між елементами та робить навігацію інтуїтивно зрозумілою. Згідно з Material Design Guidelines (2026), додатки з продуманими переходами показують на 22% вище утримання користувачів на другій сесії. Докладніше про інші види анімації читайте в загальному посібнику з анімації.
Головне
Transition — це анімація, яка відбувається при заміні одного візуального стану інтерфейсу іншим. На відміну від Property Animation (анімація властивості одного об'єкта), Transition керує цілим набором змін: поява/зникнення елементів, зміна їх положення, розміру та властивостей в рамках одного переходу. Transition вирішує проблему плавної зміни контенту так, щоб користувач розумів, що куди перемістилося.
На мобільних платформах Transition застосовується в трьох основних сценаріях: навігація між екранами (push, pop, modal present), зміна фрагментів (Android FragmentTransaction) та зміна стану всередині екрана (розгортання картки, перемикання вкладок). Кожен сценарій потребує свого підходу: навігаційні переходи — UIViewControllerAnimatedTransitioning в iOS або ActivityOptions в Android, внутрішньоекранні — Transition Framework або UIView.transition.
За даними Apple HIG (2026), тривалість стандартного переходу не повинна перевищувати 400–500 мс — довші анімації сприймаються як затримка. Material Design рекомендує 300–350 мс для мобільних переходів. Для великих елементів (картки, зображення) час може збільшуватися до 500–600 мс без втрати сприйманої продуктивності.
Типи переходів поділяються на кілька категорій залежно від того, який контент змінюється і як. Базові типи: затухання (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, з білим фоном) | Fade() через ChangeBounds |
Правила Material Design: використовуйте один тип переходу в межах одного додатку для створення єдиної візуальної мови. Рекомендується slide для навігації між екранами одного рівня (master → detail), cross-fade для зміни контенту в межах одного екрана (таби, перемикання виду), scale/zoom для переходу від картки до детального екрана (картка товару → опис).
В iOS, UIView.transition — найпростіший спосіб анімації зміни контенту всередині одного View. Він підтримує cross-dissolve, curl-up, curl-down, flip-from-left, flip-from-right. Для навігаційних переходів між UIViewController використовується UINavigationControllerDelegate з UIViewControllerAnimatedTransitioning — це дає повний контроль над анімацією 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: зміна двох View
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 використовуйте UIPercentDrivenInteractiveTransition разом з PropertyAnimator — це стандартний патерн для інтерактивної навігації.
В 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 для створення повністю кастомних анімацій переходу між контролерами. Він надає доступ до контейнерного View (transitionContext.containerView), вихідного та цільового View. У поєднанні з 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-жесті та в UIScrollView з pagingEnabled. Для кастомних жестів реалізуйте UIViewControllerInteractiveTransitioning.
Transition Framework (android.transition) — це декларативна система анімації, яка автоматично анімує зміни між двома сценами (Scene). Сцена — це набір View з певним станом. При переході від Scene A до Scene 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). Для складних переходів зі зміною структури ієрархії використовуйте TransitionSet з ordering = ORDERING_SEQUENTIAL (анімації виконуються по порядку, а не паралельно). За документацією Android Developers (2026), Transition Framework підтримує всі стандартні View, але не підтримує анімацію поверхні (SurfaceView, TextureView, VideoView).
Часті запитання
Animation — анімація властивостей одного об'єкта (alpha, translation, scale). Transition — анімація зміни набору об'єктів або станів (fragment → fragment, scene → scene). Transition керує багатьма змінами одночасно: поява, зникнення, переміщення, зміна розміру — та автоматично анімує різницю між двома станами. Animation потребує ручного керування кожною властивістю.
Використовуйте FragmentTransaction.setCustomAnimations з Transition Framework: fragment1.exitTransition = Fade(Fade.OUT), fragment2.enterTransition = Slide(Gravity.END). Для послідовності: setReorderingAllowed(true) — фрагменти будуть перевпорядковані для плавного паралельного переходу. Для простих випадків достатньо slide_in_right / slide_out_left з R.anim. Для зворотної навігації вкажіть popEnter та popExit.
Transition Framework анімує лише властивості, які не викликають перерахунок layout. Для анімації висоти використовуйте ChangeBounds — він автоматично анімує зміну меж View між двома layout-станами. Викличте TransitionManager.beginDelayedTransition(viewGroup, ChangeBounds()) перед зміною LayoutParams. Якщо висота змінюється через WRAP_CONTENT, переконайтеся, що layout_height до та після відрізняється — ChangeBounds не спрацює, якщо обидва стани match_parent.
Використовуйте модифікатор .transition() з AnyTransition: .transition(.move(edge: .trailing).combined(with: .opacity)). Для кастомних переходів реалізуйте AnyTransition.asymmetric(insertion: .move(edge: .trailing), removal: .move(edge: .leading)). Для анімації всередині NavigationStack використовуйте .navigationTransition(.slide) в iOS 18+. SwiftUI не підтримує UIViewControllerAnimatedTransitioning — лише декларативні переходи через AnyTransition.
Використовуйте Profile GPU Rendering (Android) або Core Animation Instrument (iOS). Для Android увімкніть Transition Profiling через adb shell setprop debug.transition.profiling 1 — в логах з'являться тривалості кожного етапу переходу. На iOS увімкніть Slow Animations в симуляторі. Цільові метрики: тривалість < 400 мс, FPS >= 55, frame drops — не більше 2 за перехід. Для складних сцен з 15+ одночасно анімованими View використовуйте ChangeBounds з тривалістю 300 мс.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.