CAShapeLayer est une sous-classe de CALayer conçue pour le rendu accéléré par matériel de tracés vectoriels sur le GPU. Contrairement à CALayer de base, qui travaille avec des images raster via contents, CAShapeLayer rend des formes arbitraires définies via CGPath sans créer de buffer hors écran séparé. Selon Apple QuartzCore Documentation, 2025, CAShapeLayer utilise l'accélération matérielle pour l'anticrénelage des bords et prend en charge l'animation de path, strokeStart et strokeEnd. C'est un outil standard pour créer des icônes, des indicateurs de progression et des masques dans iOS.
Points Clés
CAShapeLayer est une couche spécialisée pour le rendu de graphiques vectoriels, introduite dans iOS 3.0. Contrairement à CALayer, qui stocke une image raster, CAShapeLayer stocke une description mathématique d'un tracé — CGPath — et le rend directement sur le GPU. Cela permet de mettre à l'échelle la forme sans perte de qualité et d'animer ses propriétés sans redessiner le contenu.
Architecturalement, CAShapeLayer hérite de toutes les capacités de CALayer : ombres, bordures, transformations, masques. Mais il ajoute des propriétés spécifiques pour travailler avec les tracés : path, fillColor, strokeColor, lineWidth, lineCap, lineJoin, miterLimit, strokeStart, strokeEnd et fillRule. Chacune de ces propriétés peut être animée via CABasicAnimation.
Selon Apple Documentation, CAShapeLayer utilise l'accélération matérielle pour le rendu. Contrairement au dessin via Core Graphics dans drawRect:, où le CPU rend le tracé dans un contexte raster, CAShapeLayer envoie des commandes directement au GPU via Metal. Le résultat est des performances plus élevées, surtout pendant l'animation.
CAShapeLayer est particulièrement utile pour les éléments d'interface qui changent de forme en réponse aux actions de l'utilisateur : indicateurs de progression, graphiques, icônes de barre d'onglets, masques pour les animations de révélation de contenu.
Le processus de rendu de CAShapeLayer diffère de celui de CALayer de base. Au lieu de stocker un buffer raster avec des pixels, CAShapeLayer transmet une description de sommets du tracé au GPU — un ensemble de points, de courbes de Bézier et de segments de ligne. Le GPU rasterise ce tracé en une seule passe, appliquant les couleurs de remplissage et de trait au niveau du shader de fragments.
Cette approche offre deux avantages : la mise à l'échelle sans perte de qualité (les graphiques vectoriels restent nets à n'importe quelle résolution) et une animation efficace — lorsque le tracé change, le GPU ne recalcule que les pixels modifiés, pas tout l'écran.
path — la propriété centrale de CAShapeLayer, qui accepte un CGPath. Le tracé peut contenir des lignes, des arcs, des courbes de Bézier et des rectangles. Il est créé via UIBezierPath en Swift ou CGPath dans Core Graphics. Exemple : UIBezierPath(roundedRect: rect, cornerRadius: 16).cgPath crée un rectangle arrondi.
La propriété lineDashPattern permet de créer des lignes pointillées. Elle accepte un tableau de nombres : [6, 3] signifie 6 points de trait et 3 points d'espace. lineDashPhase définit le décalage du motif et peut être animé pour un effet de pointillé mobile.
Le choix entre CAShapeLayer et CALayer de base dépend du type de contenu. Si l'élément est une zone rectangulaire avec fond, bordure et ombre — CALayer suffit. Si l'élément a une forme arbitraire, nécessite une animation de tracé ou une mise à l'échelle sans perte de qualité — CAShapeLayer est préférable.
Les performances diffèrent également. CALayer avec cornerRadius et masksToBounds crée un buffer hors écran pour le clipping du contenu, ajoutant 2–5 ms par image. CAShapeLayer ne nécessite pas de buffer hors écran — le clipping est effectué au niveau du shader de sommets. Pour les éléments avec des masques complexes, CAShapeLayer peut être 2 à 3 fois plus rapide.
Combiner CAShapeLayer avec d'autres couches est un motif courant. Par exemple, CAGradientLayer utilise CAShapeLayer comme masque pour créer une forme avec un remplissage dégradé. Cette approche offre à la fois la qualité vectorielle et le remplissage dégradé sans perte de performances.
L'animation de path est l'une des fonctionnalités les plus puissantes de CAShapeLayer. Si les deux tracés (initial et final) ont le même nombre de points de contrôle et de segments, Core Animation effectue une interpolation fluide entre eux. Le résultat est un effet de morphing d'une forme à une autre.
Condition de compatibilité du tracé : l'UIBezierPath initial et final doivent avoir la même structure — le nombre d'appels moveTo, addLine, addCurve et addQuadCurve doit correspondre. Si la structure diffère, l'animation sera instantanée, sans interpolation.
Selon Apple WWDC 2024, la combinaison de strokeStart et strokeEnd est le motif d'animation CAShapeLayer le plus utilisé. Animer strokeEnd de 0.0 à 1.0 crée un effet de dessin du tracé du début à la fin. Cela est utilisé dans les indicateurs de chargement, les animations de révélation de graphiques et les transitions d'écran personnalisées.
Pour créer un effet de pulsation, animez strokeEnd et opacity simultanément avec différentes fonctions de synchronisation. Pour un effet de fourmis marchantes, animez lineDashPhase avec un lineDashPattern fixe. Les deux techniques sont implémentées via CABasicAnimation sans implication du CPU.
CAShapeLayer est souvent utilisé comme masque pour d'autres couches. Par exemple, pour l'animation de révélation de contenu : initialement, mask.path est un rectangle de taille zéro, et le final est de taille réelle. Core Animation interpole le tracé et le contenu apparaît en douceur.
Ce motif est utilisé dans iOS pour l'animation d'expansion de cartes, la présentation de fenêtres modales et les transitions entre contrôleurs de vue. Contrairement à l'animation alpha, l'animation de masque CAShapeLayer offre un effet de révélation plus naturel avec des formes arbitraires.
Le premier exemple — création d'un indicateur de progression circulaire avec animation strokeEnd. C'est un élément d'interface standard qui démontre les capacités principales de CAShapeLayer :
func createProgressIndicator(in view: UIView) -> CAShapeLayer {
let circlePath = UIBezierPath(
arcCenter: CGPoint(x: 50, y: 50),
radius: 40,
startAngle: -.pi / 2,
endAngle: 3 * .pi / 2,
clockwise: true
)
let trackLayer = CAShapeLayer()
trackLayer.path = circlePath.cgPath
trackLayer.strokeColor = UIColor.systemGray5.cgColor
trackLayer.fillColor = nil
trackLayer.lineWidth = 8
view.layer.addSublayer(trackLayer)
let progressLayer = CAShapeLayer()
progressLayer.path = circlePath.cgPath
progressLayer.strokeColor = UIColor.systemBlue.cgColor
progressLayer.fillColor = nil
progressLayer.lineWidth = 8
progressLayer.lineCap = .round
progressLayer.strokeEnd = 0
view.layer.addSublayer(progressLayer)
return progressLayer
}
// Animer à 75%
progressLayer.animateStrokeEnd(to: 0.75, duration: 1.5)
Le deuxième exemple — animation de morphing entre un cercle et un carré. Les deux UIBezierPath ont une structure compatible :
let morphLayer = CAShapeLayer()
morphLayer.fillColor = UIColor.systemPurple.cgColor
morphLayer.frame = rect
let circlePath = UIBezierPath(roundedRect: rect, cornerRadius: rect.width / 2)
let squarePath = UIBezierPath(roundedRect: rect, cornerRadius: 0)
morphLayer.path = circlePath.cgPath
let morphAnimation = CABasicAnimation(keyPath: "path")
morphAnimation.toValue = squarePath.cgPath
morphAnimation.duration = 0.6
morphAnimation.autoreverses = true
morphAnimation.repeatCount = .infinity
morphLayer.add(morphAnimation, forKey: "morph")
Le troisième exemple — un masque CAShapeLayer pour l'animation de révélation de contenu. Le masque s'étend du centre vers les bords :
func revealContent(_ view: UIView) {
let maskLayer = CAShapeLayer()
let size = view.bounds.size.width * 1.5
let center = CGPoint(x: view.bounds.midX, y: view.bounds.midY)
let initialPath = UIBezierPath(arcCenter: center, radius: 0, startAngle: 0, endAngle: .pi * 2, clockwise: true)
let finalPath = UIBezierPath(arcCenter: center, radius: size, startAngle: 0, endAngle: .pi * 2, clockwise: true)
maskLayer.path = initialPath.cgPath
view.layer.mask = maskLayer
let animation = CABasicAnimation(keyPath: "path")
animation.toValue = finalPath.cgPath
animation.duration = 0.4
animation.timingFunction = CAMediaTimingFunction(name: .easeOut)
maskLayer.add(animation, forKey: "reveal")
maskLayer.path = finalPath.cgPath
}
Questions Fréquemment Posées
drawRect utilise le CPU pour rendre dans un contexte raster — c'est lent pour les animations. CAShapeLayer rend le tracé sur le GPU, offrant 60 FPS lors des animations de path, strokeEnd et des transformations. De plus, CAShapeLayer ne nécessite pas de redessin lors du redimensionnement — la forme est mise à l'échelle vectoriellement.
Une animation fluide du tracé nécessite une structure identique de l'UIBezierPath initial et final. Le nombre d'appels moveTo, addLine, addCurve doit correspondre. Utilisez UIBezierPath avec le même nombre de segments pour les formes initiale et finale.
Définissez lineDashPattern — un tableau de nombres alternant les longueurs de trait et d'espace. Par exemple, [8, 4] crée des traits de 8pt avec des espaces de 4pt. Pour un effet de fourmis marchantes, animez lineDashPhase via CABasicAnimation avec une fonction de synchronisation linéaire.
Oui, c'est l'un des principaux cas d'utilisation. Assignez CAShapeLayer à la propriété mask de n'importe quel CALayer, y compris CAGradientLayer et d'autres sous-classes. Animer le tracé du masque crée un effet de révélation fluide du contenu à travers une forme arbitraire.
Créez un CAGradientLayer et définissez son mask = CAShapeLayer avec le tracé souhaité. Le dégradé ne sera visible qu'à l'intérieur du tracé du masque. Ce motif combine les avantages de la forme vectorielle de CAShapeLayer et le remplissage dégradé de CAGradientLayer.
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