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-анимацией | 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 поддерживает паузу, отмену и reverse (обратную анимацию), что важно для интерактивных переходов (swipe-to-go-back). Для анимации между UIViewController используйте UIPercentDrivenInteractiveTransition в паре с PropertyAnimator — это стандартный паттерн для интерактивной навигации.
На Android FragmentTransaction с setCustomAnimations — основной способ анимации переходов между Fragment. Параметры: enter (анимация входа нового Fragment), exit (анимация выхода старого), 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() — для показа/скрытия overlay и диалогов. Explode() — для акцентных переходов (удаление элемента из списка). Для старых устройств (API < 21) используйте R.anim ресурсы как fallback. 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 анимирует rotation и scale. ChangeImageTransform — для анимации ImageView (centerCrop → fitCenter). Для сложных переходов с изменением структуры иерархии используйте TransitionSet с ordering = ORDERING_SEQUENTIAL (анимации выполняются по порядку, а не параллельно). По документации Android Developers (2026), Transition Framework поддерживает все стандартные View, но не поддерживает анимацию поверхности (SurfaceView, TextureView, VideoView).
Часто задаваемые вопросы
Animation — анимация свойств одного объекта (alpha, translation, scale). Transition — анимация смены набора объектов или состояний (фрагмент → фрагмент, сцена → сцена). 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 с duration 300 мс.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также