Cómo agregar animación a una app iOS (SwiftUI y UIKit)
Por el equipo de Charmotion · Actualizado el

Para agregar animaciones a una app de iOS, envuelve los cambios de estado en withAnimation de SwiftUI para la respuesta visual de la interfaz, usa symbolEffect para los íconos y agrega Lottie o Rive mediante Swift Package Manager para el arte creado por diseñadores. Usa Rive cuando un personaje necesita estados, y AVPlayerLooper cuando solo necesitas un clip fijo.
Puntos clave
- La animación integrada de SwiftUI cubre casi toda la respuesta visual de la interfaz:
withAnimation,.animation(_:value:)yPhaseAnimator/KeyframeAnimatoren iOS 17+. symbolEffect(iOS 17+) anima SF Symbols, incluidos los símbolos personalizados, con un solo modificador.- Instala Lottie desde
lottie-spm, no desde el repositorio principal de más de 300 MB. RiveRuntimede Rive es compatible con iOS 14+ y permite que el código cambie los estados de un personaje en tiempo real.- Lee
accessibilityReduceMotiony reemplaza los loops por una imagen fija cuando esté activado.
¿Ya tienes un personaje o logo? Mira tu personaje en pantallas reales de una app: pantallas de splash, onboarding, carga y estado vacío. Es gratis, funciona en tu navegador y no sube nada.
¿Qué enfoque de animación conviene usar en iOS?
Empieza por qué se mueve y quién lo creó. La mayoría de las apps necesitan una combinación de cuatro enfoques:
| Quieres animar | Uso |
|---|---|
| Botones, cambios de diseño, hojas, interruptores | withAnimation de SwiftUI o UIView.animate de UIKit |
| Íconos | symbolEffect en SF Symbols (iOS 17+) |
| Un loop vectorial creado por un diseñador: confeti, un spinner, una ilustración | Lottie |
| Un personaje que reacciona a eventos de la app | Rive con una máquina de estados (state machine) |
| Un clip prerrenderizado que nunca cambia | Video con AVPlayerLooper |
Las API integradas no suman nada al peso de la app y siguen tu estado automáticamente. También son las más coherentes con el resto de iOS: los resortes, la presentación de hojas y los efectos de símbolos se sienten nativos porque son las mismas animaciones del sistema que usan las propias apps de Apple. Recurre a un runtime de terceros solo cuando el arte proviene de una herramienta de diseño. Lottie reproduce exportaciones de After Effects. Rive reproduce archivos del editor de Rive y agrega una máquina de estados, para que tu código pueda indicarle a un personaje que pase de reposo (idle) a carga y a éxito sin tener que unir clips.

¿Cómo funcionan withAnimation y .animation() en SwiftUI?
SwiftUI anima la diferencia entre dos estados. Cambia un valor dentro de withAnimation y cada vista que dependa de él se interpola hacia su nueva posición, tamaño u opacidad:
struct ExpandingCard: View {
@State private var expanded = false
var body: some View {
RoundedRectangle(cornerRadius: 16)
.frame(height: expanded ? 240 : 120)
.onTapGesture {
withAnimation(.spring()) { expanded.toggle() }
}
}
}
La alternativa es el modificador .animation(_:value:), que anima una vista cada vez que cambia el valor indicado. Usa withAnimation cuando una acción deba animar varias vistas, y el modificador cuando una vista deba animar siempre un valor específico.
Para movimientos de varios pasos, iOS 17 agregó PhaseAnimator y KeyframeAnimator. Un phase animator recorre una secuencia de fases:
Image(systemName: "heart.fill")
.phaseAnimator([false, true]) { content, phase in
content.scaleEffect(phase ? 1.2 : 1.0)
}
Usa KeyframeAnimator cuando necesites pistas de tiempo separadas para propiedades como escala, rotación y desplazamiento. Ambos sirven para pequeños loops que llaman la atención, no para personajes completos.
¿Cómo se animan los SF Symbols y las vistas de UIKit?
En iOS 17 y posteriores, el modificador .symbolEffect anima cualquier SF Symbol. Los efectos integrados de Apple incluyen bounce, pulse, variable color, scale, appear, disappear y replace, y también funcionan con símbolos personalizados:
Image(systemName: "bell.fill")
.symbolEffect(.bounce, value: unreadCount)
La campana rebota cada vez que cambia unreadCount. Es la forma más económica de dar respuesta visual a un ícono, porque no hay ningún recurso que incluir en la app.
En UIKit, la animación de vistas basada en bloques sigue funcionando y muchas veces es todo lo que necesita una pantalla:
UIView.animate(withDuration: 0.3) {
banner.alpha = 1
banner.transform = .identity
}
Úsala para fundidos, deslizamientos y cambios de escala en vistas existentes. Para cualquier cosa dibujada por un diseñador, como una ilustración o una mascota de marca, programar keyframes a mano en Swift se vuelve lento de modificar. En ese punto, un archivo de Lottie o Rive es más fácil de mantener: el diseñador edita el archivo y tu código solo decide cuándo se reproduce.
¿Cómo se agrega una animación de Lottie a una app de iOS?
En Xcode, elige File > Add Package Dependencies e ingresa https://github.com/airbnb/lottie-spm.git. Airbnb recomienda este paquete porque es un pequeño wrapper sobre binarios precompilados, mientras que el repositorio principal lottie-ios pesa más de 300 MB. Usa la versión más reciente indicada en el README.
Desde la versión 4.3.0, Lottie incluye un LottieView nativo para SwiftUI. Agrega el .json exportado a tu target y reprodúcelo:
import SwiftUI
import Lottie
struct ConfettiView: View {
var body: some View {
LottieView(animation: .named("confetti"))
.looping()
.frame(width: 200, height: 200)
}
}
La misma versión agregó compatibilidad con movimiento reducido. Lottie funciona bien para un loop de carga, una explosión de éxito o una ilustración de onboarding que se reproduce de principio a fin. Es menos adecuado para un personaje que tiene que cambiar de comportamiento en medio de un loop según lo que esté haciendo la app; ahí es donde ayudan las máquinas de estados de Rive.
Dos consejos prácticos. Mantén liviano el archivo exportado pidiéndole al diseñador que evite imágenes rasterizadas incrustadas donde funcionen los vectores. Y prueba la animación en un dispositivo más antiguo, ya que las máscaras y los efectos complejos de After Effects pueden ser costosos de renderizar aunque el archivo sea pequeño.
¿Cómo se agrega una animación de Rive y se cambian sus estados?
Agrega https://github.com/rive-app/rive-ios con Swift Package Manager (o pod 'RiveRuntime'). El runtime de Apple es compatible con iOS 14+, tvOS 16+, macOS 13.1+ y visionOS. Arrastra el .riv al target de tu app.
Con la API heredada, RiveViewModel carga el archivo, elige una máquina de estados y te da métodos para controlarla:
import SwiftUI
import RiveRuntime
struct MascotView: View {
@StateObject private var mascot = RiveViewModel(
fileName: "character",
stateMachineName: "CharacterStateMachine"
)
var body: some View {
mascot.view()
.frame(width: 220, height: 220)
}
func startLoading() { mascot.setInput("mood", value: 5.0) }
func finished(ok: Bool) {
mascot.triggerInput(ok ? "success" : "error")
mascot.setInput("mood", value: 0.0)
}
}
Guarda el view model en @StateObject para que sobreviva a los redibujados. Los nombres de los inputs distinguen mayúsculas de minúsculas y deben coincidir con el archivo.
El nuevo runtime de Apple de Rive abandona los inputs en favor de Data Binding. En su lugar, defines propiedades del view model, por ejemplo viewModelInstance.setValue(of: NumberProperty(path: "mood"), to: 5) y viewModelInstance.fire(trigger: TriggerProperty(path: "success")). Esto solo funciona si el archivo define un view model. La guía de Rive en SwiftUI explica ambos runtimes en detalle.
¿Cómo se reproduce un clip de video en loop en SwiftUI?
Si la animación es un clip fijo y prerrenderizado, como la revelación de un logo en 3D, un video es más sencillo que cualquier runtime. AVPlayerLooper reproduce un elemento de forma continua a través de un AVQueuePlayer, y VideoPlayer de SwiftUI (AVKit, iOS 14+) lo muestra:
import SwiftUI
import AVKit
final class LoopingClip: ObservableObject {
let player = AVQueuePlayer()
private var looper: AVPlayerLooper?
init(name: String) {
guard let url = Bundle.main.url(forResource: name, withExtension: "mp4") else { return }
looper = AVPlayerLooper(player: player, templateItem: AVPlayerItem(url: url))
player.isMuted = true
player.play()
}
}
struct ClipView: View {
@StateObject private var clip = LoopingClip(name: "logo-reveal")
var body: some View { VideoPlayer(player: clip.player) }
}
Mantén una referencia al looper, o el loop se detiene. Un video no puede reaccionar al estado y cada variación es otro archivo, así que úsalo para clips, no para personajes interactivos.
VideoPlayer muestra controles de reproducción. Para un loop decorativo sin controles, aloja un AVPlayerLayer en un pequeño UIViewRepresentable. Si el clip necesita un fondo transparente sobre tu interfaz, expórtalo como HEVC con canal alfa en un .mov; la comparación de formatos explica cuándo conviene más que un GIF o un archivo de Rive.
¿Cómo se agrega una mascota de marca animada a una app de iOS?
Trata la mascota como una vista del estado de tu app. Haz una lista de los momentos a los que debe reaccionar, asigna a cada uno un estado en el archivo y vincula tus eventos con ellos en un solo lugar:
- Loops para estados continuos: reposo (idle), carga, escucha, sueño.
- Triggers para eventos puntuales: éxito, error, un saludo al entrar.
En la práctica, mantén un enum pequeño como .idle, .loading y .done(Bool) en tu view model y tradúcelo en llamadas a setInput y triggerInput. La máquina de estados se encarga de las transiciones, así que nunca tienes que llevar la cuenta de qué clip está en pantalla.

Si ya tienes una mascota o un logo pero no un animador, Charmotion convierte un personaje subido en 17 animaciones, normalmente en pocos minutos; en cada plan de pago el set incluye un .riv con un view model Character (un número mood de 0 a 9 y triggers como success y error) además de código SwiftUI tanto para el RiveViewModel legacy como para el nuevo runtime. Consulta qué estados necesita una mascota para planificar el set.
¿Cómo mantener las animaciones de iOS rápidas y accesibles?
Unas pocas reglas evitan que la animación gaste batería o excluya a usuarios:
- Anima unas pocas vistas a la vez, no listas completas. Detén los loops cuando la vista sale de la pantalla.
- Prefiere vectores (SF Symbols, Lottie, Rive) para el arte pequeño de la interfaz y video para clips fotográficos o en 3D.
- Incluye el archivo de animación en el bundle de la app en lugar de descargarlo en el primer inicio, para que esté listo cuando aparezca la pantalla.
Respeta Ajustes > Accesibilidad > Movimiento > Reducir movimiento. SwiftUI lo expone como un valor de entorno que se actualiza en tiempo real:
@Environment(\.accessibilityReduceMotion) private var reduceMotion
var body: some View {
if reduceMotion {
Image("mascot-still").resizable().scaledToFit()
} else {
mascot.view()
}
}
Cuando el movimiento transmite un significado, como una carga o un éxito, mantenlo disponible como texto o como anuncio de accesibilidad. La guía de movimiento reducido explica las reglas de WCAG.
Agrega animación a una app de iOS
- Empieza con SwiftUI. Usa
withAnimationo.animation(_:value:)para el feedback de la interfaz ysymbolEffectpara los íconos. - Agrega un runtime si lo necesitas. En Xcode, agrega
https://github.com/airbnb/lottie-spm.gitpara Lottie ohttps://github.com/rive-app/rive-iospara Rive. - Incluye el archivo en el bundle. Arrastra el
.json,.rivo.mp4al proyecto y confirma que pertenece al target de la app. - Muéstrala. Usa
LottieView(animation: .named("name")).looping(),RiveViewModel(fileName:stateMachineName:).view()oVideoPlayer. - Conéctala al estado de la app. Llama a
setInputpara los loops y atriggerInputpara las reacciones puntuales cuando cambie tu view model. - Respeta Reducir movimiento. Lee
accessibilityReduceMotiony muestra una imagen fija cuando sea true.
Preguntas frecuentes
¿Debo usar UIView.animate o withAnimation?
Usa withAnimation en vistas de SwiftUI y UIView.animate en vistas de UIKit. Ambos animan cambios de propiedades en vistas existentes. La versión de SwiftUI anima todas las vistas que dependen del estado modificado, así que describes el estado final en lugar del frame de cada vista. Las apps mixtas pueden usar ambos, cada uno en su propia capa.
¿Cuál es la diferencia entre PhaseAnimator y KeyframeAnimator?
Ambos llegaron con iOS 17. PhaseAnimator recorre una lista de fases y anima entre ellas, lo que funciona bien para pulsos simples y secuencias de varios pasos. KeyframeAnimator le da a cada propiedad su propia línea de tiempo de keyframes, ideal para movimientos precisos en los que la escala, la rotación y la posición cambian en momentos distintos.
¿Lottie o Rive para una app de iOS?
Usa Lottie para loops vectoriales lineales creados por diseñadores y exportados desde After Effects. Usa Rive cuando la animación deba reaccionar al estado de la app, por ejemplo una mascota de marca que cambia entre reposo (idle), carga y éxito, porque su máquina de estados (state machine) gestiona las transiciones dentro del archivo. Ambos se instalan con Swift Package Manager y funcionan en SwiftUI.
¿Rive funciona con UIKit?
Sí. RiveRuntime es compatible con SwiftUI y con UIKit. En el runtime heredado, RiveViewModel puede crear tanto una vista de UIKit como una de SwiftUI, y las mismas llamadas a setInput y triggerInput la controlan. La documentación de Rive para Apple cubre ambas configuraciones, así que puedes combinarlas en una misma app.
Respuestas rápidas de esta guía
Fuentes
- withAnimation(_:_:), Apple
- Wind your way through advanced animations in SwiftUI (WWDC23), WWDC Notes
- Animate symbols in your app (WWDC23), Apple
- lottie-ios, Airbnb (GitHub)
- lottie-ios 4.3.0 release notes, Airbnb (GitHub)
- Apple runtime, Rive
- AVPlayerLooper, Apple
- accessibilityReduceMotion, Apple