UIBezierPath est une classe d'UIKit qui encapsule un chemin vectoriel pour les graphiques 2D sur iOS. Elle combine la description géométrique du contour avec des capacités de dessin, permettant de créer des lignes, des courbes de Bézier, des arcs et des rectangles. Contrairement à CGPath, UIBezierPath contient ses propres méthodes de dessin qui fonctionnent directement dans le contexte graphique actuel. Selon Apple Developer Documentation, 2026, UIBezierPath prend en charge toutes les opérations standard de Quartz 2D dans un wrapper orienté objet.
Points clés
UIBezierPath est une classe du framework UIKit qui hérite de NSObject et implémente NSCopying, NSSecureCoding. Elle fournit une API Objective-C/Swift de haut niveau pour construire et dessiner des chemins vectoriels. En interne, UIBezierPath stocke une référence à CGPathRef un objet Core Graphics, mais ajoute des méthodes pratiques pour le dessin, la modification de géométrie et la gestion des styles.
Le principal avantage d'UIBezierPath par rapport à l'utilisation directe de CGPath réside dans les méthodes de dessin intégrées. L'appel de fill() ou stroke() sur une instance d'UIBezierPath configure automatiquement le contexte et dessine le chemin. La classe prend également en charge les propriétés lineWidth, lineCapStyle, lineJoinStyle, miterLimit et flatness, permettant de contrôler l'apparence sans objet séparé pour stocker les styles.
UIBezierPath est utilisé dans la plupart des applications iOS pour le dessin personnalisé dans UIView : des icônes simples aux graphiques animés complexes. La méthode draw(_:) de la classe UIView est le principal endroit où les développeurs créent et dessinent UIBezierPath. La classe est entièrement compatible avec Core Animation et peut être utilisée pour l'animation de chemins via CAShapeLayer et CABasicAnimation.
La méthode move(to:) déplace le point de départ du chemin vers les coordonnées spécifiées sans dessiner. C'est un appel obligatoire avant de construire un segment. addLine(to:) ajoute un segment droit de la position actuelle au point indiqué. La méthode close() ferme le contour en traçant une ligne du dernier point au premier point du dernier move(to:).
UIBezierPath fournit plusieurs initialiseurs pratiques pour les formes prêtes à l'emploi. init(rect:) crée un rectangle, init(roundedRect:cornerRadius:) crée un rectangle aux coins arrondis. Le paramètre UIRectCorner permet d'arrondir uniquement les coins sélectionnés, comme topLeft et bottomRight, en laissant les autres droits. Ceci est largement utilisé pour créer des boutons et des cartes personnalisés.
init(ovalIn:) crée une ellipse inscrite dans le rectangle spécifié. Si le rectangle est un carré, l'ellipse devient un cercle. init(roundedRect:byRoundingCorners:cornerRadii:) est l'option la plus flexible, permettant de définir des rayons différents pour chaque coin. Cet initialiseur est apparu dans iOS 6 et reste la norme pour créer des conteneurs arrondis.
let path = UIBezierPath()
path.move(to: CGPoint(x: 50, y: 50))
path.addLine(to: CGPoint(x: 200, y: 50))
path.addLine(to: CGPoint(x: 200, y: 150))
path.addLine(to: CGPoint(x: 125, y: 200))
path.addLine(to: CGPoint(x: 50, y: 150))
path.close()
addQuadCurve(to:controlPoint:) ajoute une courbe de Bézier quadratique avec un point de contrôle. Ce type de courbe est utilisé pour des courbures douces simples lorsque l'on souhaite minimiser les calculs. Une courbe quadratique est toujours une parabole et est garantie à l'intérieur du triangle formé par les points de début, de contrôle et de fin.
addCurve(to:controlPoint1:controlPoint2:) ajoute une courbe de Bézier cubique avec deux points de contrôle. Les courbes cubiques sont utilisées dans tous les éditeurs vectoriels modernes et les polices TrueType. Deux points de contrôle offrent plus de degrés de liberté pour un contrôle précis de la forme. Cette méthode est recommandée pour construire des contours organiques et complexes.
Lors de la construction de courbes, il est important de respecter le principe de continuité la douceur au point de connexion des segments. Pour la continuité C1 (tangente), le point de contrôle de la courbe suivante doit se trouver sur la même ligne que le point de contrôle de la précédente. Pour la continuité C2 (courbure), une coordination plus complexe est nécessaire, qui est automatiquement fournie dans CAShapeLayer lors de l'animation entre deux UIBezierPath avec le même nombre de points de contrôle.
| Méthode | Type | Points de contrôle | Degré |
|---|---|---|---|
| addQuadCurve | Bézier quadratique | 1 | 2 |
| addCurve | Bézier cubique | 2 | 3 |
| addArc | Arc | — | — |
addArc(withCenter:radius:startAngle:endAngle:clockwise:) ajoute un arc de cercle avec les paramètres spécifiés. Les angles sont indiqués en radians par rapport au centre du cercle. L'angle zéro correspond à la direction vers la droite (3 heures sur un cadran d'horloge). Le paramètre clockwise détermine la direction : true dans le sens horaire, false dans le sens antihoraire. Les arcs sont indispensables pour les diagrammes circulaires, les compteurs de vitesse et les anneaux de progression interactifs.
Pour un cercle complet, utilisez startAngle = 0 et endAngle = .pi * 2. Pour créer un secteur (dans le cadre d'un diagramme circulaire), utilisez addArc suivi de addLine(to:) vers le centre et close(). UIBezierPath n'a pas de méthode addCircle intégrée un cercle est créé via init(ovalIn:) avec un rectangle carré ou via addArc avec un angle complet.
La méthode addArc(withCenter:radius:startAngle:endAngle:clockwise:) exécute automatiquement move(to:) vers le point de départ de l'arc si la position actuelle ne correspond pas. Si la position actuelle diffère, une ligne droite est tracée entre celle-ci et le début de l'arc. Ce comportement diffère de Core Graphics, où un move(to:) explicite est requis avant chaque nouveau contour.
let center = CGPoint(x: 150, y: 150)
let slice = UIBezierPath()
slice.move(to: center)
slice.addArc(
withCenter: center,
radius: 100,
startAngle: 0,
endAngle: .pi * 0.75,
clockwise: true
)
slice.close()
fill() remplit la zone intérieure du chemin avec la couleur fillColor actuelle ou la couleur définie dans le contexte graphique via setFill(). Pour les chemins avec auto-intersections, la règle de remplissage est déterminée par la propriété usesEvenOddFillRule. Si elle est true, la règle pair-impair est utilisée : un point est considéré à l'intérieur de la forme si un rayon issu de celui-ci traverse le contour un nombre impair de fois.
stroke() dessine le contour du chemin avec les paramètres actuels lineWidth, lineCapStyle et lineJoinStyle. lineCapStyle définit la forme des extrémités des segments ouverts : .butt (coupe droite), .round (semi-circulaire), .square (carré avec saillie). lineJoinStyle définit la forme des jonctions d'angles : .miter (pointu), .round (arrondi), .bevel (biseauté).
Pour les lignes pointillées, on utilise la propriété setLineDash(_:count:phase:). Elle accepte un tableau de longueurs alternées de traits et d'espaces, le nombre d'éléments et la phase initiale. Le rendu des pointillés est effectué lors de la rastérisation du chemin les motifs complexes n'augmentent pas la complexité géométrique du contour. CAShapeLayer prend en charge l'animation de la propriété strokeEnd pour un effet de «dessin» du début à la fin.
override func draw(_ rect: CGRect) {
let path = UIBezierPath(ovalIn: CGRect(x: 50, y: 50, width: 200, height: 200))
UIColor.blue.setFill()
UIColor.black.setStroke()
path.lineWidth = 4
path.fill()
path.stroke()
}
Créons un indicateur de progression personnalisé en forme d'anneau avec UIBezierPath. L'anneau est construit à partir de deux arcs de même rayon avec des couleurs de contour différentes. Le premier arc est un cercle complet en gris (fond), le second est un segment affichant la progression. Pour l'animation, on utilise CAShapeLayer et CABasicAnimation.
class RingView: UIView {
private let progressLayer = CAShapeLayer()
override func layoutSubviews() {
super.layoutSubviews()
let center = CGPoint(x: bounds.midX, y: bounds.midY)
let radius = bounds.width * 0.4
let lineWidth: CGFloat = 12
let backgroundRing = UIBezierPath()
backgroundRing.addArc(withCenter: center, radius: radius,
startAngle: -.pi / 2, endAngle: .pi * 1.5, clockwise: true)
progressLayer.path = backgroundRing.cgPath
progressLayer.strokeColor = UIColor.systemBlue.cgColor
progressLayer.lineWidth = lineWidth
progressLayer.lineCap = .round
progressLayer.fillColor = UIColor.clear.cgColor
progressLayer.strokeEnd = 0
layer.addSublayer(progressLayer)
}
func animateProgress(to value: CGFloat) {
CABasicAnimation(keyPath: "strokeEnd").apply {
$0.toValue = value
$0.duration = 1.5
$0.timingFunction = CAMediaTimingFunction(name: .easeOut)
progressLayer.add($0, forKey: nil)
}
progressLayer.strokeEnd = value
}
}
Dans cet exemple, UIBezierPath est utilisé pour créer un contour circulaire, qui est ensuite passé à CAShapeLayer via la propriété cgPath. L'animation de strokeEnd de 0 à 1 visualise le remplissage de l'anneau. Cette approche est utilisée dans les indicateurs de chargement, les trackers de fitness et les widgets de progression. La méthode layoutSubviews garantit la reconstruction du chemin lors du changement de taille de la View.
lineCap = .round ajoute des extrémités arrondies à l'arc, donnant à l'indicateur un aspect plus soigné. La propriété strokeEnd est une propriété animable de CAShapeLayer qui ne nécessite pas de redessiner tout le contour à chaque image. L'animation s'exécute entièrement sur le GPU via Core Animation, garantissant 60 FPS sans charge sur le CPU.
Questions fréquentes
UIBezierPath est un wrapper orienté objet autour de CGPath contenant des méthodes fill() et stroke() intégrées. CGPath est un objet C immuable de Core Graphics nécessitant un dessin explicite via CGContext. UIBezierPath est plus pratique pour UIKit, tandis que CGPath est plus performant pour la réutilisation.
Utilisez init(roundedRect:byRoundingCorners:cornerRadii:) avec les paramètres .topLeft et .topRight pour roundedRect. C'est la seule méthode d'UIBezierPath permettant d'arrondir sélectivement les coins sans créer un chemin composite complexe.
La propriété cgPath retourne un CGPathRef immuable qui peut être passé à CAShapeLayer ou utilisé avec différentes couleurs et épaisseurs. Les modifications d'UIBezierPath (déplacement de points) n'affectent pas le cgPath obtenu précédemment jusqu'à ce qu'il soit explicitement mis à jour.
Vérifiez usesEvenOddFillRule si le contour a des auto-intersections, la règle pair-impair peut laisser des zones non remplies. Définissez usesEvenOddFillRule = false pour la règle de remplissage standard non-zero winding.
Utilisez CABasicAnimation avec le chemin clé «path» sur CAShapeLayer. Les deux états du chemin doivent contenir le même nombre de segments et de points de contrôle sinon l'animation saccadera ou échouera.
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