Transition es un mecanismo de cambio visual que reemplaza un contenido por otro: pantallas, fragmentos, vistas o estados de interfaz. Una transición bien implementada dirige la atención del usuario, explica la relación entre elementos y hace que la navegación sea intuitiva. Según Material Design Guidelines (2026), las aplicaciones con transiciones bien diseñadas muestran un 22% más de retención de usuarios en la segunda sesión. Obtenga más información sobre otros tipos de animación en la guía general de animación.
Puntos clave
Transition es una animación que ocurre al reemplazar un estado visual de interfaz por otro. A diferencia de la Property Animation (animar una propiedad de un solo objeto), Transition gestiona un conjunto completo de cambios: aparición/desaparición de elementos, cambio de su posición, tamaño y propiedades dentro de una sola transición. Transition resuelve el problema del cambio suave de contenido para que el usuario entienda qué se movió y hacia dónde.
En plataformas móviles, Transition se utiliza en tres escenarios principales: navegación entre pantallas (push, pop, modal present), cambio de fragmentos (FragmentTransaction de Android) y cambio de estado dentro de una pantalla (expansión de tarjeta, cambio de pestañas). Cada escenario requiere su propio enfoque: transiciones de navegación — UIViewControllerAnimatedTransitioning en iOS o ActivityOptions en Android, transiciones dentro de la pantalla — Transition Framework o UIView.transition.
Según Apple HIG (2026), la duración de una transición estándar no debe exceder los 400–500 ms — las animaciones más largas se perciben como retraso. Material Design recomienda 300–350 ms para transiciones móviles. Para elementos grandes (tarjetas, imágenes), el tiempo puede aumentar a 500–600 ms sin pérdida de rendimiento percibido.
Los tipos de transición se dividen en varias categorías según qué contenido cambia y cómo. Tipos básicos: cross-fade (fundido) — desaparición suave de uno y aparición de otro; slide (deslizamiento) — una pantalla sale, otra entra; scale/zoom (escalado) — un elemento se agranda, abriendo una pantalla de detalle. Las transiciones combinadas mezclan varios efectos.
| Tipo de transición | Descripción | API iOS | API Android |
|---|---|---|---|
| Cross-fade | Fundido: un contenido desaparece, otro aparece | UIView.transition(with:duration:options:.transitionCrossDissolve) | TransitionSet + Fade() |
| Slide | Deslizamiento: el contenido entra/sale horizontal o verticalmente | CATransition(type: .push, subtype: .fromLeft) | Slide() / FragmentTransaction.setCustomAnimations |
| Scale / Zoom | Escalado: el elemento se agranda a pantalla completa | UIViewPropertyAnimator con animación de escala | Scale() + ActivityOptions.makeScaleUpAnimation |
| Explode | Explosión: el contenido se fragmenta al desaparecer | CATransition(type: .reveal) | Explode() (API 21+) |
| Fade-through | Fundido a través de pantalla blanca/negra | UIView.transition(with: .transitionCrossDissolve, con fondo blanco) | Fade() mediante ChangeBounds |
Reglas de Material Design: utilice un solo tipo de transición dentro de una aplicación para crear un lenguaje visual unificado. Recomendado: slide para navegación entre pantallas del mismo nivel (master → detalle), cross-fade para cambio de contenido dentro de una pantalla (pestañas, cambio de vista), scale/zoom para la transición de una tarjeta a una pantalla de detalle (tarjeta de producto → descripción).
En iOS, UIView.transition es la forma más sencilla de animar cambios de contenido dentro de una sola View. Admite cross-dissolve, curl-up, curl-down, flip-from-left, flip-from-right. Para transiciones de navegación entre UIViewController, utilice UINavigationControllerDelegate con UIViewControllerAnimatedTransitioning — esto da control total sobre la animación push/pop.
import UIKit
// UIView.transition: cambio de imagen con 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: intercambio de dos vistas
UIView.transition(from: firstView, to: secondView,
duration: 0.5,
options: [.transitionFlipFromRight, .showHideTransitionViews]
) { _ in
// firstView oculta, secondView mostrada
}
// CATransition para capa (sin 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 es una alternativa moderna para transiciones personalizadas en iOS 10+. A diferencia de UIView.transition, PropertyAnimator admite pausa, cancelación y animación inversa, lo cual es importante para transiciones interactivas (deslizar para volver). Para la animación entre UIViewControllers, use UIPercentDrivenInteractiveTransition junto con PropertyAnimator — este es el patrón estándar para navegación interactiva.
En Android, FragmentTransaction con setCustomAnimations es la forma principal de animar transiciones entre Fragment. Parámetros: enter (animación de entrada del nuevo Fragment), exit (animación de salida del Fragment antiguo), popEnter y popExit — para navegación hacia atrás. FragmentTransaction admite recursos de animación (R.anim) y Transition Framework (R.transition).
import androidx.fragment.app.FragmentTransaction
import androidx.transition.TransitionInflater
// FragmentTransaction con animaciones de recursos
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 con 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: animación al iniciar una Activity
val options = ActivityOptions.makeScaleUpAnimation(
view, 0, 0, view.width, view.height
).toBundle()
startActivity(intent, options)
Mejores prácticas de Android: Slide() — para reemplazar Fragment en navegación (enter = Slide(Gravity.END), exit = Slide(Gravity.START)). Fade() — para mostrar/ocultar superposiciones y diálogos. Explode() — para transiciones de énfasis (eliminar un elemento de una lista). Para dispositivos antiguos (API < 21) use recursos R.anim como fallback. Transition Framework no admite SurfaceView, TextureView, GLSurfaceView — para video y cámara use solo animaciones R.anim.
UIViewControllerAnimatedTransitioning es un protocolo de iOS para crear animaciones de transición completamente personalizadas entre controladores. Proporciona acceso a la vista contenedora (transitionContext.containerView), la vista de origen y la vista de destino. Combinado con UIPercentDrivenInteractiveTransition, este enfoque permite crear transiciones interactivas con desplazamiento táctil.
import UIKit
"> Animador de transición personalizado
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)
}
}
}
// Aplicación en UINavigationControllerDelegate
extension MainViewController: UINavigationControllerDelegate {
func navigationController(
_ nc: UINavigationController,
animationControllerFor operation: UINavigationController.Operation,
from: UIViewController, to: UIViewController
) -> UIViewControllerAnimatedTransitioning? {
return ScaleTransitionAnimator(presenting: operation == .push)
}
}
Transiciones interactivas: agregue un UIPanGestureRecognizer a la pantalla de origen. Cuando se active el gesto, cree un UIPercentDrivenInteractiveTransition y llame a update(CGFloat(percent)). Para cancelar cuando no se alcance el umbral (generalmente 30%), llame a cancel(). Este patrón se usa por defecto en UINavigationController para gestos de pop y en UIScrollView con pagingEnabled. Para gestos personalizados, implemente UIViewControllerInteractiveTransitioning.
Transition Framework (android.transition) es un sistema de animación declarativo que anima automáticamente los cambios entre dos escenas (Scene). Una escena es un conjunto de Views con un estado específico. Al pasar de la Escena A a la Escena B, Transition Framework analiza la diferencia y anima: cambio de posición (ChangeBounds), cambio de tamaño (ChangeClipBounds, ChangeTransform), aparición (Fade) y desaparición (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: combinación de ChangeBounds + Fade
val transition = TransitionSet().apply {
ordering = TransitionSet.ORDERING_TOGETHER
addTransition(ChangeBounds())
addTransition(Fade(Fade.IN))
duration = 400L
interpolator = FastOutSlowInInterpolator()
}
// Transición entre escenas en un 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 sin configuración manual
TransitionManager.go(scene2, AutoTransition().apply {
duration = 350L
})
// TransitionManager.beginDelayedTransition — sin escenas
TransitionManager.beginDelayedTransition(sceneRoot, ChangeBounds())
itemView.layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT
itemView.requestLayout() "> El cambio de layout se anima automáticamente
ChangeBounds es la transición más utilizada en Android, anima cambios de posición y tamaño de View. ChangeTransform anima rotación y escala. ChangeImageTransform — para animación de ImageView (centerCrop → fitCenter). Para transiciones complejas con cambios en la estructura de jerarquía, use TransitionSet con ordering = ORDERING_SEQUENTIAL (las animaciones se ejecutan en secuencia, no en paralelo). Según la documentación de Android Developers (2026), Transition Framework admite todas las Views estándar pero no admite animación de superficie (SurfaceView, TextureView, VideoView).
Preguntas frecuentes
Animation — anima propiedades de un solo objeto (alpha, translation, scale). Transition — anima el cambio de un conjunto de objetos o estados (fragment → fragment, scene → scene). Transition gestiona múltiples cambios simultáneamente: aparición, desaparición, movimiento, cambio de tamaño — y anima automáticamente la diferencia entre dos estados. Animation requiere gestión manual de cada propiedad.
Use FragmentTransaction.setCustomAnimations con Transition Framework: fragment1.exitTransition = Fade(Fade.OUT), fragment2.enterTransition = Slide(Gravity.END). Para secuenciación: setReorderingAllowed(true) — los fragmentos se reordenarán para una transición paralela suave. Para casos simples, slide_in_right / slide_out_left de R.anim es suficiente. Para navegación hacia atrás, especifique popEnter y popExit.
Transition Framework solo anima propiedades que no desencadenan recálculo de layout. Para animación de altura, use ChangeBounds — anima automáticamente los cambios de límites de View entre dos estados de layout. Llame a TransitionManager.beginDelayedTransition(viewGroup, ChangeBounds()) antes de cambiar LayoutParams. Si la altura cambia mediante WRAP_CONTENT, asegúrese de que layout_height difiera antes y después — ChangeBounds no funcionará si ambos estados son match_parent.
Use el modificador .transition() con AnyTransition: .transition(.move(edge: .trailing).combined(with: .opacity)). Para transiciones personalizadas, implemente AnyTransition.asymmetric(insertion: .move(edge: .trailing), removal: .move(edge: .leading)). Para animación dentro de NavigationStack, use .navigationTransition(.slide) en iOS 18+. SwiftUI no admite UIViewControllerAnimatedTransitioning — solo transiciones declarativas mediante AnyTransition.
Use Profile GPU Rendering (Android) o Core Animation Instrument (iOS). Para Android, active Transition Profiling mediante adb shell setprop debug.transition.profiling 1 — los registros mostrarán la duración de cada etapa de transición. En iOS, active Slow Animations en el simulador. Métricas objetivo: duración < 400 ms, FPS >= 55, frame drops — no más de 2 por transición. Para escenas complejas con 15+ Views animadas simultáneamente, use ChangeBounds con duración de 300 ms.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también