tutoriales.com

Domina Core Animation: Creando Animaciones Impresionantes en iOS con Swift

Este tutorial te guiará a través del poderoso framework Core Animation de Apple para iOS. Descubrirás cómo transformar tus interfaces estáticas en experiencias dinámicas e interactivas utilizando animaciones de capa, transiciones y efectos visuales avanzados, todo ello programado en Swift.

Intermedio20 min de lectura19 views
Reportar error
Domina Core Animation: Creando Animaciones Impresionantes en iOS con Swift

Core Animation es la piedra angular de toda la magia visual que vemos en iOS. Es el motor que impulsa las animaciones fluidas, las transiciones suaves y los efectos visuales que hacen que las aplicaciones de Apple sean tan atractivas. Aunque SwiftUI y UIKit ofrecen sus propias formas de animar vistas, entender Core Animation te da un control mucho más granular y te permite crear efectos que van más allá de las capacidades de alto nivel.

En este tutorial, profundizaremos en Core Animation, explorando sus conceptos clave, cómo interactúa con CALayer y UIView, y cómo puedes aprovecharlo para crear animaciones personalizadas y de alto rendimiento en tus propias aplicaciones iOS utilizando Swift.


🚀 ¿Qué es Core Animation?

Core Animation es un framework de composición y animación que te permite animar las propiedades de los objetos de capa (CALayer). No es una API de dibujo en sí misma, sino más bien un sistema de renderizado que organiza y compone el contenido de tus capas, permitiendo que la GPU realice gran parte del trabajo pesado para lograr animaciones fluidas a 60 FPS (o 120 FPS en dispositivos ProMotion).

Cada UIView en iOS está respaldada por una instancia de CALayer. Cuando cambias propiedades como la posición, el tamaño o el color de fondo de una vista, en realidad estás cambiando las propiedades de su CALayer subyacente. Core Animation intercepta estos cambios y puede animarlos por ti.

Propiedades animables comunes de CALayer:

  • position
  • bounds
  • transform (escalado, rotación, traslación 3D)
  • opacity
  • backgroundColor
  • cornerRadius
  • borderWidth, borderColor
  • shadowOpacity, shadowOffset, shadowRadius, shadowColor
💡 Consejo: Piensa en `CALayer` como el *modelo* visual, mientras que `UIView` es el *controlador* que gestiona la interacción y la representación de una o más capas.

🛠️ Configurando tu Proyecto

Para empezar, abriremos Xcode y crearemos un nuevo proyecto de tipo App para iOS. Puedes elegir la interfaz UIKit App Delegate o SwiftUI App, aunque la mayoría de los ejemplos se centrarán en UIKit para demostrar la interacción directa con CALayer.

  1. Abre Xcode.
  2. Selecciona Create a new Xcode project.
  3. Elige la plantilla iOS -> App.
  4. Nombra tu proyecto (ej. CoreAnimationDemo).
  5. Asegúrate de que la interfaz sea Storyboard o Programmatic si quieres seguir los ejemplos de UIKit, o SwiftUI si prefieres integrar con SwiftUI (algunos ejemplos se adaptarán). El idioma debe ser Swift.
  6. Guarda el proyecto.

Para este tutorial, utilizaremos un UIViewController simple donde añadiremos y manipularemos CALayers programáticamente.


🎨 Animaciones Implícitas vs. Explícitas

Core Animation ofrece dos tipos principales de animaciones:

🎭 Animaciones Implícitas

Las animaciones implícitas son las más sencillas. Ocurren automáticamente cuando cambias una propiedad animable de CALayer fuera de una CATransaction explícita, y Core Animation decide cómo animar ese cambio. Esto es lo que sucede cuando animas UIView utilizando UIView.animate.

Por defecto, CALayer tiene acciones implícitas asociadas a sus propiedades animables. Cuando se modifica una de estas propiedades, Core Animation busca una CAAction asociada y la ejecuta. En el contexto de un UIView y su capa subyacente, UIView deshabilita muchas de estas acciones implícitas por defecto para tener un control más preciso.

Ejemplo Básico de Animación Implícita (sin UIView.animate):

Crearemos una CALayer y la animaremos cambiando su color de fondo.

import UIKit

class ImplicitAnimationViewController: UIViewController {

    let animatedLayer = CALayer()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white

        animatedLayer.frame = CGRect(x: 50, y: 100, width: 100, height: 100)
        animatedLayer.backgroundColor = UIColor.blue.cgColor
        view.layer.addSublayer(animatedLayer)

        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap))
        view.addGestureRecognizer(tapGesture)
    }

    @objc func handleTap() {
        // Animación implícita por defecto para CALayer
        animatedLayer.backgroundColor = (animatedLayer.backgroundColor == UIColor.blue.cgColor) ? UIColor.red.cgColor : UIColor.blue.cgColor
        animatedLayer.cornerRadius = (animatedLayer.cornerRadius == 0) ? 50 : 0
    }
}

Si ejecutas este código, verás que los cambios en backgroundColor y cornerRadius se animan suavemente. Esto se debe a que CALayer tiene acciones implícitas configuradas para estas propiedades.

📌 Nota: Cuando trabajas con `UIView`, las animaciones implícitas para muchas propiedades de su `layer` están deshabilitadas por defecto. Por eso usamos `UIView.animate` para animar vistas. `UIView.animate` crea internamente `CATransaction`s para agrupar los cambios de propiedades y aplicar animaciones explícitas.

🎬 Animaciones Explícitas (CAAnimation)

Las animaciones explícitas te dan control total. Tú defines exactamente cómo y cuándo ocurre la animación. Esto se logra creando instancias de CAAnimation y sus subclases, configurándolas y luego añadiéndolas a un CALayer.

Las subclases principales de CAAnimation son:

  • CABasicAnimation: Anima una única propiedad de la capa de un valor a otro.
  • CAKeyframeAnimation: Anima una propiedad a través de una serie de valores clave en momentos específicos.
  • CAAnimationGroup: Combina múltiples animaciones y las ejecuta simultáneamente.
  • CATransition: Realiza transiciones de contenido o efectos visuales, como fade, push o move.

CABasicAnimation: Animando una Propiedad Única

CABasicAnimation es la forma más común de animar una propiedad. Especificas una keyPath (la propiedad a animar), un fromValue y un toValue.

Ejemplo: Animando la posición de una capa

import UIKit

class BasicAnimationViewController: UIViewController {

    let animatedLayer = CALayer()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white

        animatedLayer.frame = CGRect(x: 50, y: 100, width: 100, height: 100)
        animatedLayer.backgroundColor = UIColor.systemTeal.cgColor
        view.layer.addSublayer(animatedLayer)

        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap))
        view.addGestureRecognizer(tapGesture)
    }

    @objc func handleTap() {
        let basicAnimation = CABasicAnimation(keyPath: "position.x") // Animamos la posición X
        basicAnimation.fromValue = animatedLayer.position.x
        basicAnimation.toValue = animatedLayer.position.x + 200
        basicAnimation.duration = 1.0 // Duración de 1 segundo
        basicAnimation.repeatCount = 1 // Ejecutar una vez
        basicAnimation.autoreverses = true // Volver al estado inicial
        basicAnimation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut) // Curva de aceleración

        // Es crucial que el modelo (la propiedad de la capa) se actualice al final de la animación
        // para que la capa permanezca en su estado final.
        // Si no lo haces, la capa volverá a su estado original una vez finalizada la animación.
        // animatedLayer.position.x = animatedLayer.position.x + 200 // Esto solo para el estado final si no autoreverses

        animatedLayer.add(basicAnimation, forKey: "moveLayer")
    }
}
🔥 Importante: Core Animation anima una *copia de presentación* de la capa. Una vez que la animación termina, la capa de presentación se elimina. Si quieres que la capa se quede en su estado final, debes actualizar explícitamente la propiedad subyacente de la capa.

Para el ejemplo anterior, si autoreverses es false, deberías hacer animatedLayer.position.x = animatedLayer.position.x + 200 al final de la animación o antes de añadirla para que la capa no "salte" de vuelta.

CAKeyframeAnimation: Animación por Fotogramas Clave

CAKeyframeAnimation te permite especificar una serie de valores (fotogramas clave) a lo largo del tiempo. Esto es útil para crear animaciones más complejas o trayectorias personalizadas.

Ejemplo: Animando una capa a lo largo de una trayectoria

import UIKit

class KeyframeAnimationViewController: UIViewController {

    let animatedLayer = CALayer()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white

        animatedLayer.frame = CGRect(x: 50, y: 100, width: 50, height: 50)
        animatedLayer.backgroundColor = UIColor.systemOrange.cgColor
        animatedLayer.cornerRadius = 25
        view.layer.addSublayer(animatedLayer)

        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap))
        view.addGestureRecognizer(tapGesture)
    }

    @objc func handleTap() {
        let keyframeAnimation = CAKeyframeAnimation(keyPath: "position")
        keyframeAnimation.duration = 2.0
        keyframeAnimation.repeatCount = .infinity // Animación infinita

        let path = UIBezierPath()
        path.move(to: animatedLayer.position)
        path.addCurve(to: CGPoint(x: view.bounds.width - 50, y: 200), controlPoint1: CGPoint(x: 150, y: 50), controlPoint2: CGPoint(x: view.bounds.width - 150, y: 300))
        path.addLine(to: CGPoint(x: 50, y: 400))
        path.addLine(to: animatedLayer.position)

        keyframeAnimation.path = path.cgPath
        keyframeAnimation.calculationMode = .paced // Distribuye el tiempo de manera uniforme

        animatedLayer.add(keyframeAnimation, forKey: "pathAnimation")
    }
}

Este ejemplo animará la capa siguiendo una trayectoria definida por UIBezierPath. calculationMode = .paced intenta mantener una velocidad constante a lo largo de la trayectoria.

CAAnimation CABasicAnimation CAKeyframeAnimation CAAnimationGroup CATransition

CAAnimationGroup: Combinando Múltiples Animaciones

Para animar múltiples propiedades simultáneamente, puedes usar CAAnimationGroup. Esto es muy útil para coordinar efectos complejos.

Ejemplo: Rotación, escalado y cambio de color al mismo tiempo

import UIKit

class AnimationGroupViewController: UIViewController {

    let animatedLayer = CALayer()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white

        animatedLayer.frame = CGRect(x: 50, y: 100, width: 100, height: 100)
        animatedLayer.backgroundColor = UIColor.systemGreen.cgColor
        animatedLayer.cornerRadius = 10
        view.layer.addSublayer(animatedLayer)

        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap))
        view.addGestureRecognizer(tapGesture)
    }

    @objc func handleTap() {
        // Animación de rotación
        let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
        rotationAnimation.toValue = CGFloat.pi * 2.0 // 360 grados
        rotationAnimation.duration = 2.0

        // Animación de escalado
        let scaleAnimation = CABasicAnimation(keyPath: "transform.scale")
        scaleAnimation.toValue = 1.5
        scaleAnimation.duration = 2.0
        scaleAnimation.autoreverses = true

        // Animación de color de fondo
        let colorAnimation = CABasicAnimation(keyPath: "backgroundColor")
        colorAnimation.toValue = UIColor.systemPurple.cgColor
        colorAnimation.duration = 2.0
        colorAnimation.autoreverses = true

        // Grupo de animaciones
        let groupAnimation = CAAnimationGroup()
        groupAnimation.animations = [rotationAnimation, scaleAnimation, colorAnimation]
        groupAnimation.duration = 4.0 // La duración total del grupo, ajusta según autoreverses
        groupAnimation.repeatCount = .infinity

        animatedLayer.add(groupAnimation, forKey: "combinedAnimations")
    }
}

CATransition: Efectos de Transición

CATransition se utiliza para crear transiciones visuales entre diferentes estados de una capa o para cambiar el contenido de una capa con un efecto animado.

Ejemplo: Transición de imagen en una capa

import UIKit

class TransitionAnimationViewController: UIViewController {

    let imageLayer = CALayer()
    var isFirstImage = true

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white

        imageLayer.frame = CGRect(x: 50, y: 100, width: 200, height: 200)
        imageLayer.contentsGravity = .resizeAspectFill
        imageLayer.masksToBounds = true
        view.layer.addSublayer(imageLayer)

        updateImage(image: UIImage(systemName: "photo.fill")!)

        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap))
        view.addGestureRecognizer(tapGesture)
    }

    func updateImage(image: UIImage) {
        imageLayer.contents = image.cgImage
    }

    @objc func handleTap() {
        let transition = CATransition()
        transition.type = .fade // Otros tipos: .push, .moveIn, .reveal
        transition.subtype = .fromRight // Subtipo para .push, .moveIn, .reveal
        transition.duration = 1.0
        transition.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)

        imageLayer.add(transition, forKey: "imageTransition")

        if isFirstImage {
            updateImage(image: UIImage(systemName: "star.fill")!)
        } else {
            updateImage(image: UIImage(systemName: "photo.fill")!)
        }
        isFirstImage.toggle()
    }
}

Tabla de tipos de CATransition y subtype comunes:

transition.typeDescripcióntransition.subtype (ejemplos)
.fadeFundido de entrada/salidanil
.pushEmpuja el nuevo contenido desde un borde.fromRight, .fromLeft
.moveInMueve el nuevo contenido sobre el existente.fromTop, .fromBottom
.revealRevela el nuevo contenido detrás del existente.fromRight, .fromLeft
kCATransitionCubeEfecto de cubo (privado, usar con precaución).fromTop, .fromBottom
⚠️ Advertencia: Algunos tipos de transición (`kCATransitionCube`, `kCATransitionSuckEffect`, etc.) no son parte de la API pública y pueden cambiar o dejar de funcionar en futuras versiones de iOS. Usa los tipos públicos (`.fade`, `.push`, etc.) para mayor compatibilidad y seguridad.

🔄 CAMediaTimingFunction: Controlando la Velocidad de la Animación

La timingFunction de una animación define cómo cambia la velocidad de la animación a lo largo del tiempo. Esto le da a tus animaciones una sensación más natural y pulida.

  • .linear: Velocidad constante.
  • .easeIn: Empieza lento y acelera.
  • .easeOut: Empieza rápido y desacelera.
  • .easeInEaseOut: Empieza lento, acelera en el medio y desacelera al final.
  • default: Lo mismo que .easeInEaseOut.

También puedes crear tus propias funciones de temporización con puntos de control Bézier cúbicos:

// Una función de temporización personalizada (curva de Bézier cúbica)
let customTiming = CAMediaTimingFunction(controlPoints: 0.1, 0.7, 1.0, 0.1)
animation.timingFunction = customTiming

🎯 CAAnimationDelegate: Respondiendo a Eventos de Animación

Para ser notificado cuando una animación comienza o termina, puedes asignar un delegado a tu objeto CAAnimation.

import UIKit

class AnimationDelegateViewController: UIViewController, CAAnimationDelegate {

    let animatedLayer = CALayer()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white

        animatedLayer.frame = CGRect(x: 50, y: 100, width: 100, height: 100)
        animatedLayer.backgroundColor = UIColor.systemBlue.cgColor
        view.layer.addSublayer(animatedLayer)

        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap))
        view.addGestureRecognizer(tapGesture)
    }

    @objc func handleTap() {
        let basicAnimation = CABasicAnimation(keyPath: "position.x")
        basicAnimation.toValue = animatedLayer.position.x + 200
        basicAnimation.duration = 1.5
        basicAnimation.delegate = self // Asignamos el delegado

        animatedLayer.add(basicAnimation, forKey: "delegateAnimation")
    }

    // MARK: - CAAnimationDelegate Methods

    func animationDidStart(_ anim: CAAnimation) {
        print("Animación iniciada: \(anim.debugDescription)")
        // Puedes deshabilitar interacciones o mostrar un indicador de actividad aquí
    }

    func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
        print("Animación detenida: \(anim.debugDescription), finalizada: \(flag)")
        if flag {
            // La animación terminó completamente
            // Actualizar el estado final de la capa si es necesario
            if let basicAnim = anim as? CABasicAnimation, basicAnim.keyPath == "position.x",
               let toValue = basicAnim.toValue as? CGFloat {
                animatedLayer.position.x = toValue
            }
        } else {
            // La animación fue cancelada (ej. otra animación la reemplazó)
        }
    }
}
📌 Nota: Es importante recordar actualizar la propiedad del *modelo* de la capa (`animatedLayer.position.x = toValue`) en `animationDidStop` si quieres que la capa se quede en su estado final después de que la animación de *presentación* haya terminado.

✨ Transformaciones 3D con CATransform3D

Core Animation te permite aplicar transformaciones 3D a tus capas utilizando la propiedad transform de CALayer, que es de tipo CATransform3D.

CATransform3D es una matriz 4x4 que se utiliza para realizar escalado, rotación, traslación y otras transformaciones 3D.

Ejemplo: Rotación 3D y Perspectiva

import UIKit
import QuartzCore // Para CATransform3D

class Transform3DViewController: UIViewController {

    let cubeLayer = CALayer()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white

        cubeLayer.frame = CGRect(x: (view.bounds.width - 100) / 2, y: 200, width: 100, height: 100)
        cubeLayer.backgroundColor = UIColor.systemBlue.cgColor
        cubeLayer.borderWidth = 2
        cubeLayer.borderColor = UIColor.white.cgColor
        view.layer.addSublayer(cubeLayer)

        // Añadir gestos para interactuar con la rotación
        let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePan))
        view.addGestureRecognizer(panGesture)
    }

    @objc func handlePan(_ gesture: UIPanGestureRecognizer) {
        let translation = gesture.translation(in: view)
        let rotationAngleX = translation.y / view.bounds.height * CGFloat.pi * 2 // Proporción de altura para rotación X
        let rotationAngleY = translation.x / view.bounds.width * CGFloat.pi * 2 // Proporción de ancho para rotación Y

        // Configurar la perspectiva
        var transform = CATransform3DIdentity
        transform.m34 = -1.0 / 500.0 // Valor crucial para la perspectiva (distancia al "observador")

        // Aplicar rotaciones en X e Y
        transform = CATransform3DRotate(transform, rotationAngleX, 1, 0, 0) // Rotar alrededor del eje X
        transform = CATransform3DRotate(transform, rotationAngleY, 0, 1, 0) // Rotar alrededor del eje Y

        cubeLayer.transform = transform

        if gesture.state == .ended {
            // Resetear la traslación para el próximo arrastre
            gesture.setTranslation(.zero, in: view)
        }
    }
}
¿Qué es `m34` en `CATransform3D`?La propiedad `m34` de la matriz `CATransform3D` es fundamental para crear un efecto de perspectiva. Actúa como el inverso de la distancia al observador (o cámara). Un valor de `-1.0 / 500.0` significa que el observador está a 500 puntos de distancia. Sin un valor no nulo para `m34`, las transformaciones 3D solo parecerán 2D, ya que no habrá profundidad aparente.

🛑 Pausar y Reanudar Animaciones

Puedes pausar y reanudar animaciones en una capa manipulando su speed y timeOffset.

  • layer.speed = 0.0: Pausa la animación.
  • layer.timeOffset = <current_time_of_animation>: Guarda el punto donde se pausó.
  • layer.speed = 1.0: Reanuda la animación.
  • layer.beginTime = CACurrentMediaTime() - layer.timeOffset: Ajusta el beginTime para que la animación continúe desde donde se pausó.

Ejemplo: Botón de Pausar/Reanudar

import UIKit

class PauseResumeViewController: UIViewController {

    let animatedLayer = CALayer()
    var isPaused = false

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white

        animatedLayer.frame = CGRect(x: 50, y: 100, width: 100, height: 100)
        animatedLayer.backgroundColor = UIColor.systemIndigo.cgColor
        view.layer.addSublayer(animatedLayer)

        // Añadir una animación de rotación infinita
        let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
        rotationAnimation.toValue = CGFloat.pi * 2.0
        rotationAnimation.duration = 3.0
        rotationAnimation.repeatCount = .infinity
        animatedLayer.add(rotationAnimation, forKey: "rotatingAnimation")

        // Botón para pausar/reanudar
        let toggleButton = UIButton(type: .system)
        toggleButton.setTitle("Pausar", for: .normal)
        toggleButton.frame = CGRect(x: (view.bounds.width - 100) / 2, y: 400, width: 100, height: 50)
        toggleButton.addTarget(self, action: #selector(toggleAnimation), for: .touchUpInside)
        view.addSubview(toggleButton)
    }

    @objc func toggleAnimation(sender: UIButton) {
        if isPaused {
            // Reanudar
            let pausedTime = animatedLayer.timeOffset
            animatedLayer.speed = 1.0
            animatedLayer.timeOffset = 0.0
            animatedLayer.beginTime = 0.0
            let timeSincePause = animatedLayer.convertTime(CACurrentMediaTime(), from: nil) - pausedTime
            animatedLayer.beginTime = timeSincePause
            sender.setTitle("Pausar", for: .normal)
        } else {
            // Pausar
            let pausedTime = animatedLayer.convertTime(CACurrentMediaTime(), from: nil)
            animatedLayer.speed = 0.0
            animatedLayer.timeOffset = pausedTime
            sender.setTitle("Reanudar", for: .normal)
        }
        isPaused.toggle()
    }
}

📈 Rendimiento y Buenas Prácticas

Crear animaciones fluidas es crucial para una buena experiencia de usuario. Aquí tienes algunas consideraciones para el rendimiento:

  • Propiedades animables que afectan al rendimiento: Animar propiedades que requieren que la CPU vuelva a calcular el layout (frame, bounds, position si afecta al layout de otras vistas) puede ser costoso. Propiedades como transform (rotación, escala, traslación) y opacity son mucho más eficientes porque se gestionan directamente por la GPU.
  • Offscreen Rendering: Algunas propiedades como shadows y cornerRadius combinadas con masksToBounds = true pueden activar el offscreen rendering, lo que puede ralentizar la animación. Considera optimizaciones o evita estas combinaciones si el rendimiento es crítico.
  • Caché de Capas: Para capas complejas que no cambian a menudo, puedes rasterizarlas usando layer.shouldRasterize = true. Core Animation creará una imagen de mapa de bits de la capa y la animará, lo que puede mejorar el rendimiento. Asegúrate de establecer layer.rasterizationScale = UIScreen.main.scale para evitar que se vea pixelado.
// Ejemplo de rasterización
animatedLayer.shouldRasterize = true
animatedLayer.rasterizationScale = UIScreen.main.scale
⚠️ Advertencia: Usa `shouldRasterize` con precaución. Si el contenido de la capa cambia con frecuencia, la rasterización constante puede ser más costosa que no rasterizar en absoluto.
90% Optimización con GPU

🔗 Integración con SwiftUI

Aunque Core Animation es más directamente accesible en UIKit, puedes usar capas Core Animation en SwiftUI a través de UIViewRepresentable o CALayer. Esto te permite traer animaciones personalizadas y complejas que SwiftUI por sí mismo podría no ofrecer de forma directa.

Ejemplo: CALayer en SwiftUI

Podrías crear un UIViewRepresentable que contenga tu CALayer y luego animar sus propiedades.

import SwiftUI

struct CoreAnimationView: UIViewRepresentable {
    @Binding var animate: Bool

    func makeUIView(context: Context) -> UIView {
        let view = UIView()
        view.backgroundColor = .clear

        let layer = CALayer()
        layer.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
        layer.backgroundColor = UIColor.blue.cgColor
        layer.cornerRadius = 10
        view.layer.addSublayer(layer)

        context.coordinator.animatedLayer = layer

        return view
    }

    func updateUIView(_ uiView: UIView, context: Context) {
        if animate {
            context.coordinator.startAnimation()
        } else {
            context.coordinator.stopAnimation()
        }
    }

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    class Coordinator: NSObject {
        var parent: CoreAnimationView
        var animatedLayer: CALayer?

        init(_ parent: CoreAnimationView) {
            self.parent = parent
        }

        func startAnimation() {
            guard let layer = animatedLayer else { return }
            
            let basicAnimation = CABasicAnimation(keyPath: "position.y")
            basicAnimation.toValue = layer.position.y + 100
            basicAnimation.duration = 1.0
            basicAnimation.autoreverses = true
            basicAnimation.repeatCount = .infinity
            layer.add(basicAnimation, forKey: "bounceAnimation")
        }

        func stopAnimation() {
            animatedLayer?.removeAnimation(forKey: "bounceAnimation")
        }
    }
}

struct ContentView: View {
    @State private var shouldAnimate = false

    var body: some View {
        VStack {
            CoreAnimationView(animate: $shouldAnimate)
                .frame(width: 200, height: 200)
                .border(Color.gray)

            Toggle(isOn: $shouldAnimate) {
                Text("Animar")
            }
            .padding()
        }
    }
}

Este ejemplo básico muestra cómo incrustar un CALayer dentro de una vista SwiftUI y controlarlo desde el estado de SwiftUI. Para animaciones más avanzadas o complejas, la integración podría requerir más lógica dentro del coordinador o del UIViewRepresentable.

UIView Gestión de Eventos y Layout Configura Contenido Informa al Delegado CALayer Renderizado y Animación Nivel Superior Nivel Inferior

🔚 Conclusión

Core Animation es una herramienta increíblemente potente y flexible para crear animaciones de alta calidad en iOS. Aunque puede parecer complejo al principio, dominar sus conceptos te abrirá un mundo de posibilidades para enriquecer la experiencia de usuario de tus aplicaciones.

Desde las animaciones implícitas más sencillas hasta las complejas trayectorias de CAKeyframeAnimation y las transformaciones 3D, Core Animation te proporciona el control y el rendimiento necesarios para hacer que tus apps cobren vida. Practica con los ejemplos, experimenta con diferentes propiedades y funciones de temporización, y pronto estarás creando interfaces dinámicas e impresionantes.

¡Anímate a explorar todas las posibilidades que Core Animation tiene para ofrecer! 🚀

Tutoriales relacionados

Comentarios (0)

Aún no hay comentarios. ¡Sé el primero!