CGContext est l'objet principal de Core Graphics pour exécuter des commandes de dessin 2D dans iOS et macOS. Il représente un contexte graphique qui stocke l'état actuel : couleur de remplissage, épaisseur de trait, transformation, zone de découpage et polices. Toutes les opérations de dessin — lignes, rectangles, texte, images — sont effectuées via CGContext. Selon Apple Developer Documentation, 2026, CGContext est le fondement de tous les graphiques raster et vectoriels dans l'écosystème Apple.
Points Clés
CGContext est un type opaque du framework Core Graphics qui représente un environnement pour exécuter des commandes de rendu 2D. Il gère la pile d'états, l'espace colorimétrique actuel, la transformation des coordonnées et la zone de découpage. CGContext est le seul objet avec lequel toutes les fonctions de dessin Quartz 2D interagissent — toute opération graphique passe par lui.
CGContext existe en plusieurs variétés selon son objectif : bitmap context pour travailler avec des images raster en mémoire, PDF context pour générer des documents PDF, window context pour le dessin à l'écran (fourni par UIView dans draw(_:)). Les développeurs iOS travaillent le plus souvent avec un contexte obtenu à partir de UIGraphicsGetCurrentContext() dans la méthode draw(_:) ou de UIGraphicsBeginImageContextWithOptions pour le rendu hors ligne.
Le contexte applique tous les paramètres via une pile d'états — le développeur peut sauvegarder l'état actuel (saveGState), modifier les paramètres et restaurer l'état précédent (restoreGState). C'est d'une importance critique lors du dessin de scènes complexes où différents éléments nécessitent différentes couleurs, épaisseurs de trait et transformations. Selon Apple WWDC 2023, une utilisation appropriée de la pile d'états améliore la lisibilité du code et prévient les artefacts visuels.
CGContextSaveGState place une copie de l'état actuel du contexte sur la pile. Cet état inclut tous les paramètres : couleurs, épaisseur de trait, transformation, ombres, zone de découpage, polices. Après modification des paramètres, l'appel à CGContextRestoreGState ramène le contexte à l'état sauvegardé, annulant toutes les modifications intermédiaires. La pile peut contenir jusqu'à 32 états sur iOS.
Lors du dessin de groupes d'éléments avec différents styles (par exemple, des secteurs de graphique multicolores), la pile d'états élimine la nécessité de mémoriser et restaurer manuellement chaque paramètre. Sans saveGState/restoreGState, le développeur devrait réinitialiser la couleur, l'ombre et la transformation après chaque élément. L'utilisation de la pile réduit le code et prévient les erreurs de restauration.
Il est important de maintenir un équilibre des sauvegardes et restaurations — chaque saveGState doit avoir un restoreGState correspondant. Rompre l'équilibre entraîne des fuites d'état ou une restauration prématurée, provoquant un rendu imprévisible. L'analyseur statique de Xcode ne vérifie pas l'équilibre de la pile CGContext, donc l'exactitude relève de la responsabilité du développeur. Il est recommandé de regrouper les paires dans des blocs do/finally.
override func draw(_ rect: CGRect) {
guard let ctx = UIGraphicsGetCurrentContext() else { return }
ctx.saveGState()
ctx.setStrokeColor(UIColor.red.cgColor)
ctx.setLineWidth(3)
ctx.addRect(CGRect(x: 50, y: 50, width: 200, height: 100))
ctx.strokePath()
ctx.restoreGState()
}
CGContextSetFillColorWithColor définit la couleur de remplissage pour les opérations suivantes. La couleur est passée en tant que CGColorRef, qui contient des informations sur l'espace colorimétrique et les composantes de couleur. CGContextSetStrokeColorWithColor fonctionne de manière similaire pour la couleur de trait. Apple recommande de mettre en cache les valeurs CGColorRef fréquemment utilisées pour améliorer les performances.
CGContextDrawLinearGradient dessine un dégradé linéaire entre deux points avec des couleurs spécifiées. CGContextDrawRadialGradient crée un dégradé radial passant d'un cercle à un autre. Pour travailler avec des dégradés, un objet CGGradientRef est d'abord créé contenant un tableau de couleurs et de positions d'arrêt. Les dégradés dans CGContext prennent en charge les espaces colorimétriques RVB et Niveaux de Gris.
Remplir une zone avec un dégradé nécessite d'abord de définir une zone de découpage. Sans clip, le dégradé remplit tout le contexte, pas seulement la forme souhaitée. Le motif typique : ajouter un chemin au contexte, appeler clip pour restreindre la zone, puis dessiner le dégradé. Ce motif est utilisé dans toutes les applications nécessitant des remplissages dégradés de formes complexes, des boutons aux arrière-plans.
| Fonction | Objectif | Espace colorimétrique |
|---|---|---|
| setFillColor | Définit la couleur de remplissage | Quelconque (via CGColor) |
| setStrokeColor | Définit la couleur de trait | Quelconque (via CGColor) |
| drawLinearGradient | Dessine un dégradé linéaire | RVB ou Niveaux de Gris |
| drawRadialGradient | Dessine un dégradé radial | RVB ou Niveaux de Gris |
CGContextAddPath ajoute un CGPathRef préalablement créé au contexte pour le rendu ultérieur. Après avoir ajouté un chemin, il peut être tracé (strokePath), rempli (fillPath) ou les deux. CGContextBeginPath commence un nouveau chemin, effaçant le précédent. Le contexte ne prend en charge qu'un seul chemin actif à la fois.
CGContextMoveToPoint déplace le point de contour actuel vers les coordonnées spécifiées. CGContextAddLineToPoint dessine une ligne droite du point actuel au point donné. CGContextAddCurveToPoint ajoute une courbe de Bézier cubique, CGContextAddQuadCurveToPoint ajoute une courbe quadratique. CGContextClosePath ferme le contour avec une ligne droite entre le dernier et le premier point.
Contrairement à UIBezierPath, où toutes ces méthodes sont appelées sur l'objet chemin, dans CGContext elles sont appelées directement sur le contexte. Cette différence est importante lors du choix d'une approche : UIBezierPath est plus pratique pour réutiliser les chemins, CGContext est meilleur pour un dessin unique avec une surcharge minimale. CGContext prend également en charge les lignes pointillées via CGContextSetLineDash, les extrémités flexibles via CGContextSetLineCap et les jonctions via CGContextSetLineJoin.
let ctx = UIGraphicsGetCurrentContext()!
ctx.beginPath()
ctx.moveTo(x: 50, y: 100)
ctx.addLineTo(x: 200, y: 100)
ctx.addCurveTo(x1: 250, y1: 50, x2: 300, y2: 150, x3: 350, y3: 100)
ctx.setStrokeColor(UIColor.blue.cgColor)
ctx.setLineWidth(5)
ctx.strokePath()
CGContextClip restreint la zone de dessin au chemin actuel. Toutes les opérations de dessin ultérieures ne seront visibles qu'à l'intérieur de cette zone. C'est le mécanisme de base pour le masquage, l'arrondi des coins et la création de limites personnalisées d'éléments. La zone de découpage est également sauvegardée et restaurée via saveGState/restoreGState.
CGContextTranslateCTM déplace l'origine du système de coordonnées du contexte. CGContextScaleCTM met à l'échelle les axes X et Y. CGContextRotateCTM fait pivoter le système de coordonnées autour de l'origine actuelle. Les transformations s'accumulent — chacune est appliquée par rapport au système de coordonnées actuel. Les transformations composites (translation + rotation + échelle) sont utilisées pour l'animation et le positionnement des éléments.
Combiner le découpage et les transformations offre des capacités puissantes : vous pouvez dessiner un masque complexe via le découpage, puis appliquer une transformation à tout le contenu. Un exemple typique est l'affichage d'une partie d'image en angle avec des coins arrondis. L'ordre des opérations est important : d'abord définir la zone de découpage, puis appliquer la transformation, puis dessiner le contenu. Apple recommande de minimiser les changements de CTM (Matrice de Transformation Courante) dans la boucle de dessin.
override func draw(_ rect: CGRect) {
guard let ctx = UIGraphicsGetCurrentContext() else { return }
ctx.saveGState()
// clipping region - circle
ctx.addEllipse(in: CGRect(x: 50, y: 50, width: 200, height: 200))
ctx.clip()
// content transformation
ctx.translateBy(x: 150, y: 150)
ctx.rotate(by: .pi / 4)
ctx.translateBy(x: -150, y: -150)
UIImage(named: "photo")?.draw(at: CGPoint(x: 50, y: 50))
ctx.restoreGState()
}
Examinons un exemple complet de création d'un graphique dégradé avec CGContext. Le graphique comprend trois secteurs de différentes couleurs avec des remplissages dégradés et des étiquettes de texte. Le code démontre la combinaison de saveGState, clip, dégradés et rendu de texte dans un seul contexte.
class PieChartView: UIView {
private struct Slice {
let color: UIColor
let value: CGFloat
let label: String
}
override func draw(_ rect: CGRect) {
guard let ctx = UIGraphicsGetCurrentContext() else { return }
let slices = [
Slice(color: .systemRed, value: 0.4, label: "iOS"),
Slice(color: .systemGreen, value: 0.35, label: "Android"),
Slice(color: .systemBlue, value: 0.25, label: "Other")
]
let center = CGPoint(x: bounds.midX, y: bounds.midY)
let radius = bounds.width * 0.35
var startAngle: CGFloat = -.pi / 2
for slice in slices {
let endAngle = startAngle + .pi * 2 * slice.value
ctx.saveGState()
ctx.moveTo(x: center.x, y: center.y)
ctx.addArc(center: center, radius: radius,
startAngle: startAngle, endAngle: endAngle, clockwise: false)
ctx.closePath()
ctx.setFillColor(slice.color.cgColor)
ctx.fillPath()
// text label
let midAngle = startAngle + endAngle / 2
let labelPos = CGPoint(
x: center.x + radius * 0.7 * cos(midAngle),
y: center.y + radius * 0.7 * sin(midAngle)
)
slice.label.draw(at: labelPos, withAttributes: [
.foregroundColor: UIColor.white,
.font: UIFont.boldSystemFont(ofSize: 14)
])
ctx.restoreGState()
startAngle = endAngle
}
}
}
Dans cet exemple, chaque secteur du graphique est dessiné dans son propre bloc saveGState/restoreGState. Cette approche garantit que les paramètres de couleur et de chemin d'un secteur n'affectent pas les autres. La méthode addArc crée un arc de l'angle de début à l'angle de fin, et closePath ferme le secteur vers le centre. Le texte de l'étiquette est placé à l'angle médian du secteur en utilisant les fonctions trigonométriques cos et sin pour le calcul de la position.
Pour améliorer les performances lors des redessins fréquents, il est recommandé de mettre en cache les calculs d'angles et de positions dans un tableau séparé. CGContext exécute toutes les opérations sur le CPU, donc les scènes complexes avec des dizaines de secteurs peuvent nécessiter une optimisation — par exemple, le pré-rendu dans CGLayer ou l'utilisation de Metal pour l'accélération GPU. Pour la plupart des tâches d'interface utilisateur avec 5 à 10 éléments, les performances de CGContext restent acceptables.
Questions Fréquentes
Appelez UIGraphicsGetCurrentContext() à l'intérieur de la méthode draw(_:) de UIView ou à l'intérieur d'un bloc UIGraphicsBeginImageContextWithOptions. Pour les couches CALayer, utilisez la méthode draw(in:) de CALayerDelegate, qui reçoit un contexte prêt.
fillPath remplit l'intérieur du chemin actuel avec la couleur de setFillColor. strokePath dessine une ligne le long du chemin avec les paramètres de setStrokeColor, setLineWidth et setLineCap. Les deux opérations sont exécutées séquentiellement : d'abord le remplissage, puis le trait si nécessaire.
Bitmap CGContext est un contexte créé via CGBitmapContextCreate qui dessine dans un tampon mémoire au lieu de l'écran. Il est utilisé pour la génération d'images en arrière-plan, le traitement graphique sans affichage et la création de textures pour Metal ou OpenGL.
Core Graphics ne fournit pas de méthode pour réinitialiser tous les paramètres. Utilisez saveGState avant les modifications et restoreGState pour revenir à l'état d'origine. Alternativement, créez un nouveau contexte avec les mêmes paramètres.
Avant d'appeler drawLinearGradient, définissez la zone de découpage via clip sur le chemin souhaité. Sans clip, le dégradé remplit tout le contexte. Ordre : addPath → clip → drawLinearGradient — garantit le dégradé à l'intérieur de la forme.
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