Core Animation est le framework d’animation bas niveau d’Apple qui opère au niveau de CALayer et rend le contenu sur le GPU. Core Animation est la base de toutes les animations sous iOS et macOS : UIKit, AppKit, SceneKit et SpriteKit sont construits par-dessus. Cet article explique l’architecture de CALayer, CABasicAnimation, CATransaction et les techniques pratiques.
Points clés
Core Animation est le framework graphique d’Apple introduit dans Mac OS X 10.5 Leopard (2007) et porté sur iOS avec la première version d’iPhone OS (2007). Core Animation gère la composition, l’animation et le rendu des couches (CALayer) sur le GPU. Chaque UIView sous iOS possède un CALayer intégré (view.layer) par lequel tout le dessin s’effectue.
Core Animation fonctionne selon le principe de l’animation implicite : certaines modifications de propriétés de CALayer s’animent automatiquement (CABasicAnimation pour position, bounds, opacity, backgroundColor). Les animations implicites utilisent CATransaction par défaut avec une durée de 0,25 seconde et une courbe easeInOut.
Selon l’Apple WWDC 2024, Core Animation traite jusqu’à 120 FPS sur les appareils ProMotion, en utilisant Metal pour le rendu GPU. L’empreinte mémoire d’une couche typique est de 4 octets par pixel (RGBA) + ~100 octets de surcharge par couche. Il est recommandé de ne pas dépasser 1000 couches à l’écran pour des 60 FPS stables.
CALayer est le fondement de Core Animation. Chaque CALayer contient un contenu bitmap (ou une référence de texture GPU) et définit la géométrie (bounds, position, anchorPoint, transform, cornerRadius, borderWidth, shadow). Les couches ne gèrent pas les événements tactiles — UIView/UIScrollView s’en chargent. Les couches forment une hiérarchie (layer tree) qui reflète la hiérarchie des vues.
Core Animation maintient trois arbres de couches : le model layer tree (propriétés réelles), le presentation layer tree (affichage actuel pendant l’animation) et le render tree (données GPU). La couche de présentation est utilisée pour lire les valeurs d’animation intermédiaires en temps réel.
CALayer possède des sous-classes spécialisées : CAShapeLayer (graphiques vectoriels), CAGradientLayer (dégradé), CATextLayer (texte), CATiledLayer (grandes images divisées en tuiles), CAEAGLLayer/MetalLayer (OpenGL/Metal). CAShapeLayer est le plus populaire pour l’animation personnalisée grâce au support de CGPath et à l’animation strokeStart/strokeEnd.
CABasicAnimation est une sous-classe de CAAnimation qui anime une seule propriété de CALayer d’une valeur de départ (fromValue) à une valeur finale (toValue) ou par un changement relatif (byValue). L’animation est ajoutée à la couche via layer.add(animation, forKey:) et s’exécute dans le serveur de rendu sans implication de l’application à chaque image.
La clé (keyPath) est une chaîne spécifiant la propriété : opacity, position, bounds, transform.rotation.z, cornerRadius, shadowOpacity, etc. Core Animation prend en charge plus de 60 keyPaths animables. Pour les propriétés personnalisées, CATransaction et la couche de présentation sont utilisées.
import QuartzCore
// CABasicAnimation pour opacity et 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. Animation d’opacité
let fadeAnim = CABasicAnimation(keyPath: "opacity")
fadeAnim.fromValue = 1.0
fadeAnim.toValue = 0.2
fadeAnim.duration = 2.0
fadeAnim.autoreverses = true
fadeAnim.repeatCount = .infinity
// 2. Animation de rayon de coin
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")
}
// Animation de position avec 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 // mise à jour du model layer
}Important : CABasicAnimation est un affichage temporaire (presentation layer). Une fois l’animation terminée, la couche revient à ses valeurs du model layer. Pour figer l’état final, définissez isRemovedOnCompletion = false, fillMode = .forwards et mettez à jour le model layer avec les valeurs finales.
CATransaction est un mécanisme de regroupement des animations Core Animation en une seule transaction. CATransaction.begin() et CATransaction.commit() définissent les limites de la transaction. À l’intérieur d’une transaction, des paramètres partagés peuvent être définis : duration, timingFunction, completionBlock, disableActions. CATransaction est le fondement des animations implicites de CALayer.
L’animation implicite se produit lorsqu’une propriété de CALayer est modifiée en dehors d’un bloc d’animation : layer.opacity = 0,5. Core Animation crée automatiquement une CABasicAnimation avec la durée de la CATransaction actuelle (par défaut 0,25 s). Pour désactiver l’animation implicite, utilisez CATransaction.setDisableActions(true).
// CATransaction — regroupement et achèvement
func groupedAnimation() {
CATransaction.begin()
CATransaction.setAnimationDuration(1.5)
CATransaction.setAnimationTimingFunction(
CAMediaTimingFunction(name: .easeOut)
)
CATransaction.setCompletionBlock {
print("All animations completed")
}
// Tous les changements dans la transaction sont animés avec une durée de 1.5
layer1.opacity = 0.3
layer2.position = CGPoint(x: 200, y: 300)
layer3.backgroundColor = UIColor.systemRed.cgColor
CATransaction.commit()
}
// Désactivation de l’animation implicite
func updateWithoutAnimation() {
CATransaction.begin()
CATransaction.setDisableActions(true)
layer.frame = newFrame // sans animation
CATransaction.commit()
}
// Animation des propriétés 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 s’intègre à UIKit via la couche UIView. L’exemple ci-dessous montre une animation CAShapeLayer — une progression circulaire avec animation de longueur d’arc et rotation de couche via 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 anime la propriété strokeEnd de 0 à 1, créant un effet de « remplissage » de progression circulaire. CATransaction avec une durée de 0,6 et easeOut rend l’animation fluide. layer.lineCap = .round ajoute des extrémités de ligne arrondies pour un aspect plus net.
Questions fréquentes
Core Animation fonctionne au niveau de CALayer et est contrôlé via CABasicAnimation et CATransaction. UIView.animate est un wrapper de haut niveau autour de Core Animation qui crée automatiquement CABasicAnimation pour les propriétés UIView. Core Animation offre plus de contrôle : keyPath, timingFunction, fillMode, regroupement via CATransaction.
L’animation implicite est une animation automatique qui se produit lorsqu’une propriété animable de CALayer est modifiée en dehors d’un bloc d’animation. Par exemple, layer.opacity = 0,5 crée une CABasicAnimation avec une durée de 0,25 s. Les animations implicites sont gérées via CATransaction et peuvent être désactivées avec setDisableActions(true).
Pour les propriétés personnalisées, implémentez action(forKey:) dans CALayer et retournez une CAAnimation. Sinon, utilisez CALayer.display() pour le dessin manuel avec animation via CADisplayLink. Pour les propriétés qui prennent en charge CABasicAnimation, spécifiez simplement le keyPath comme une chaîne.
CALayer est plus léger que UIView : il n’a pas de gestionnaires d’événements (touches, gestures), ne participe pas à Auto Layout et ne prend pas en charge l’accessibilité. Un CALayer typique prend ~100 octets contre ~300+ pour UIView. Core Animation traite les couches dans le serveur de rendu sur le GPU sans bloquer le thread principal.
Résumé
Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.
Lisez aussi