Core Animation es el framework de animación de bajo nivel de Apple que opera a nivel de CALayer y renderiza contenido en la GPU. Core Animation es la base de todas las animaciones en iOS y macOS: UIKit, AppKit, SceneKit y SpriteKit están construidos sobre él. Este artículo explica la arquitectura de CALayer, CABasicAnimation, CATransaction y técnicas prácticas.
Puntos clave
Core Animation es el framework gráfico de Apple introducido en Mac OS X 10.5 Leopard (2007) y portado a iOS con la primera versión de iPhone OS (2007). Core Animation gestiona la composición, animación y renderizado de capas (CALayer) en la GPU. Cada UIView en iOS tiene un CALayer incorporado (view.layer) a través del cual ocurre todo el dibujo.
Core Animation opera bajo el principio de animación implícita: algunos cambios de propiedades de CALayer se animan automáticamente (CABasicAnimation para position, bounds, opacity, backgroundColor). Las animaciones implícitas usan CATransaction por defecto con una duración de 0.25 segundos y una curva easeInOut.
Según Apple WWDC 2024, Core Animation procesa hasta 120 FPS en dispositivos ProMotion, utilizando Metal para el renderizado en GPU. El consumo de memoria de una capa típica es de 4 bytes por píxel (RGBA) + ~100 bytes de overhead por capa. Se recomienda no superar las 1000 capas en pantalla para mantener 60 FPS estables.
CALayer es la base de Core Animation. Cada CALayer contiene contenido de mapa de bits (o una referencia a textura de GPU) y define la geometría (bounds, position, anchorPoint, transform, cornerRadius, borderWidth, shadow). Las capas no manejan eventos táctiles — UIView/UIScrollView se encargan de eso. Las capas forman una jerarquía (layer tree) que refleja la jerarquía de vistas.
Core Animation mantiene tres árboles de capas: el model layer tree (propiedades reales), el presentation layer tree (visualización actual durante la animación) y el render tree (datos para GPU). La capa de presentación se utiliza para leer valores intermedios de animación en tiempo real.
CALayer tiene subclases especializadas: CAShapeLayer (gráficos vectoriales), CAGradientLayer (gradiente), CATextLayer (texto), CATiledLayer (imágenes grandes divididas en mosaicos), CAEAGLLayer/MetalLayer (OpenGL/Metal). CAShapeLayer es el más popular para animación personalizada gracias al soporte de CGPath y la animación de strokeStart/strokeEnd.
CABasicAnimation es una subclase de CAAnimation que anima una única propiedad de CALayer desde un valor inicial (fromValue) hasta un valor final (toValue) o mediante un cambio relativo (byValue). La animación se añade a la capa mediante layer.add(animation, forKey:) y se ejecuta en el render server sin participación de la aplicación en cada fotograma.
La clave (keyPath) es una cadena que especifica la propiedad: opacity, position, bounds, transform.rotation.z, cornerRadius, shadowOpacity, etc. Core Animation admite más de 60 keyPaths animables. Para propiedades personalizadas se utilizan CATransaction y la capa de presentación.
import QuartzCore
// CABasicAnimation para opacity y cornerRadius
func animateLayer() {
let layer = CALayer()
layer.frame = CGRect(x: 50, y: 50, width: 100, height: 100)
layer.backgroundColor = UIColor.systemBlue.cgColor
view.layer.addSublayer(layer)
// 1. Animación de opacidad
let fadeAnim = CABasicAnimation(keyPath: "opacity")
fadeAnim.fromValue = 1.0
fadeAnim.toValue = 0.2
fadeAnim.duration = 2.0
fadeAnim.autoreverses = true
fadeAnim.repeatCount = .infinity
// 2. Animación de radio de esquina
let cornerAnim = CABasicAnimation(keyPath: "cornerRadius")
cornerAnim.fromValue = 0
cornerAnim.toValue = 50
cornerAnim.duration = 2.0
cornerAnim.autoreverses = true
cornerAnim.repeatCount = .infinity
layer.add(fadeAnim, forKey: "fade")
layer.add(cornerAnim, forKey: "corner")
}
// Animación de position con ease-in-out
func moveLayer(_ layer: CALayer, to point: CGPoint) {
let anim = CABasicAnimation(keyPath: "position")
anim.fromValue = NSValue(cgPoint: layer.position)
anim.toValue = NSValue(cgPoint: point)
anim.duration = 0.6
anim.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
anim.isRemovedOnCompletion = false
anim.fillMode = .forwards
layer.add(anim, forKey: "move")
layer.position = point // actualizando model layer
}Importante: CABasicAnimation es una visualización temporal (presentation layer). Después de que la animación se completa, la capa vuelve a sus valores del model layer. Para fijar el estado final, establezca isRemovedOnCompletion = false, fillMode = .forwards y actualice el model layer a los valores finales.
CATransaction es un mecanismo para agrupar animaciones de Core Animation en una sola transacción. CATransaction.begin() y CATransaction.commit() definen los límites de la transacción. Dentro de una transacción se pueden establecer parámetros compartidos: duration, timingFunction, completionBlock, disableActions. CATransaction es la base de las animaciones implícitas de CALayer.
La animación implícita ocurre cuando una propiedad de CALayer cambia fuera de un bloque de animación: layer.opacity = 0.5. Core Animation crea automáticamente una CABasicAnimation con la duración del CATransaction actual (por defecto 0.25 s). Para deshabilitar la animación implícita, use CATransaction.setDisableActions(true).
// CATransaction — agrupación y finalización
func groupedAnimation() {
CATransaction.begin()
CATransaction.setAnimationDuration(1.5)
CATransaction.setAnimationTimingFunction(
CAMediaTimingFunction(name: .easeOut)
)
CATransaction.setCompletionBlock {
print("All animations completed")
}
// Todos los cambios dentro de la transacción se animan con duración 1.5
layer1.opacity = 0.3
layer2.position = CGPoint(x: 200, y: 300)
layer3.backgroundColor = UIColor.systemRed.cgColor
CATransaction.commit()
}
// Deshabilitando animación implícita
func updateWithoutAnimation() {
CATransaction.begin()
CATransaction.setDisableActions(true)
layer.frame = newFrame // sin animación
CATransaction.commit()
}
// Animando propiedades de CAShapeLayer
func animateProgress(percentage: CGFloat) {
CATransaction.begin()
CATransaction.setAnimationDuration(0.8)
CATransaction.setCompletionBlock {
print("Progress updated to (percentage)%")
}
progressLayer.strokeEnd = percentage / 100.0
CATransaction.commit()
}Core Animation se integra con UIKit a través de la capa UIView. El ejemplo siguiente demuestra la animación de CAShapeLayer — un progreso circular con animación de longitud de arco y rotación de capa mediante CABasicAnimation.
import UIKit
import QuartzCore
class CircularProgressView: UIView {
private let progressLayer = CAShapeLayer()
override func layoutSubviews() {
super.layoutSubviews()
let path = UIBezierPath(
arcCenter: CGPoint(x: bounds.midX, y: bounds.midY),
radius: bounds.width / 2 - 10,
startAngle: -.pi / 2,
endAngle: 3 * .pi / 2,
clockwise: true
)
progressLayer.path = path.cgPath
progressLayer.strokeColor = UIColor.systemBlue.cgColor
progressLayer.lineWidth = 8
progressLayer.fillColor = nil
progressLayer.strokeEnd = 0
progressLayer.lineCap = .round
layer.addSublayer(progressLayer)
}
func setProgress(_ value: CGFloat, animated: Bool = true) {
if animated {
CATransaction.begin()
CATransaction.setAnimationDuration(0.6)
CATransaction.setAnimationTimingFunction(
CAMediaTimingFunction(name: .easeOut)
)
progressLayer.strokeEnd = min(max(value, 0), 1)
CATransaction.commit()
} else {
progressLayer.strokeEnd = value
}
}
}CAShapeLayer anima la propiedad strokeEnd de 0 a 1, creando un efecto de “llenado” de progreso circular. CATransaction con duración 0.6 y easeOut hace que la animación sea suave. layer.lineCap = .round agrega extremos redondeados a la línea para un aspecto más limpio.
Preguntas frecuentes
Core Animation funciona a nivel de CALayer y se controla mediante CABasicAnimation y CATransaction. UIView.animate es un envoltorio de alto nivel sobre Core Animation que crea automáticamente CABasicAnimation para las propiedades de UIView. Core Animation ofrece más control: keyPath, timingFunction, fillMode, agrupación mediante CATransaction.
La animación implícita es una animación automática que ocurre cuando se cambia una propiedad animable de CALayer fuera de un bloque de animación. Por ejemplo, layer.opacity = 0.5 crea una CABasicAnimation con duración de 0.25 s. Las animaciones implícitas se gestionan mediante CATransaction y se pueden deshabilitar usando setDisableActions(true).
Para propiedades personalizadas, implemente action(forKey:) en CALayer y devuelva una CAAnimation. Alternativamente, use CALayer.display() para dibujo manual con animación mediante CADisplayLink. Para propiedades que admiten CABasicAnimation, simplemente especifique el keyPath como una cadena.
CALayer es más ligero que UIView: no tiene manejadores de eventos (touches, gestures), no participa en Auto Layout y no soporta accesibilidad. Un CALayer típico ocupa ~100 bytes frente a ~300+ de UIView. Core Animation procesa las capas en el render server en la GPU sin bloquear el hilo principal.
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