draw(_:) / drawRect est une méthode de la classe UIView (en Swift) et son équivalent en Objective-C (drawRect:), responsable du rendu du contenu de la vue à l'aide de Core Graphics. Le système appelle cette méthode automatiquement lors de la première apparition de la vue à l'écran et après l'appel de setNeedsDisplay(). Selon la Documentation Apple (2025), draw(_:) est la seule méthode où le développeur a accès au contexte graphique de l'écran actuel pour un dessin personnalisé. Redéfinir draw(_:) donne un contrôle total sur l'apparence du composant — des formes géométriques simples aux graphiques animés complexes.
Points clés
draw(_:) est une méthode d'instance de UIView que UIKit appelle pour rendre le contenu de la vue. À l'intérieur de cette méthode, le développeur obtient l'accès au contexte graphique CGContext et utilise l'API Core Graphics pour dessiner des lignes, des remplissages, du texte et des images. drawRect: en Objective-C remplit la même fonction mais avec une syntaxe différente : le seul paramètre est un CGRect spécifiant la zone à redessiner.
Selon Apple Engineering (2024), draw(_:) fonctionne via un rendu CPU sur Bitmap Graphics Context, ce qui offre une flexibilité maximale mais nécessite plus de ressources par rapport à CALayer. La décision d'utiliser draw(_:) est prise en fonction de la complexité des graphiques et des exigences de performance.
En Swift, la méthode est déclarée comme override func draw(_ rect: CGRect), où rect est le rectangle qui doit être redessiné. En Objective-C, la signature est - (void)drawRect:(CGRect)rect. Le paramètre rect peut être plus petit que les bounds de la vue lors d'un redessin partiel via setNeedsDisplay(_:).
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
CGContextFillRect(context, rect);
}
Le système appelle automatiquement draw(_:) dans des scénarios strictement définis. Comprendre ces déclencheurs permet d'éviter les redessins inutiles et d'optimiser les performances de la vue. Voici tous les cas d'invocation automatique de la méthode.
La documentation Apple avertit : n'appelez jamais draw(_:) directement. Le système décide lui-même quand effectuer le dessin, et un appel direct perturbe le mécanisme interne de cache. Pour demander un redessin, utilisez toujours setNeedsDisplay() ou setNeedsDisplay(_:).
La redéfinition de draw(_:) en Swift commence par l'obtention du contexte graphique et les appels ultérieurs à Core Graphics. Il est recommandé de créer des méthodes séparées pour les blocs logiques de dessin — cela améliore la lisibilité et la testabilité du code.
override func draw(_ rect: CGRect) {
super.draw(rect)
guard let context = UIGraphicsGetCurrentContext() else { return }
// Line parameters
context.setStrokeColor(UIColor.darkGray.cgColor)
context.setLineWidth(2.0)
// Drawing a triangle
context.move(to: CGPoint(x: rect.midX, y: rect.minY + 10))
context.addLine(to: CGPoint(x: rect.maxX - 10, y: rect.maxY - 10))
context.addLine(to: CGPoint(x: rect.minX + 10, y: rect.maxY - 10))
context.closePath()
context.strokePath()
}
Dans cet exemple, draw(_:) dessine un triangle avec un contour gris foncé de 2 pixels d'épaisseur. L'appel à super.draw(rect) au début de la méthode est recommandé par Apple pour préserver la logique de dessin parent, bien que l'implémentation par défaut de draw(_:) dans UIView soit vide.
Chaque appel à draw(_:) doit produire un résultat identique avec les mêmes données d'entrée. Cela permet au système de mettre en cache le résultat et de ne pas rappeler draw(_:) si le contenu de la vue n'a pas changé. N'utilisez pas de valeurs aléatoires, d'heure système ou de requêtes réseau à l'intérieur de draw(_:).
drawRect: est historiquement la première version de la méthode, apparue dans iOS 2.0 avec Objective-C. En Swift, la méthode a été renommée en draw(_:) en utilisant le paramètre externe _. Fonctionnellement, les méthodes sont identiques : toutes deux reçoivent un CGRect de la zone à redessiner et utilisent UIGraphicsGetCurrentContext() pour accéder au contexte graphique.
| Caractéristique | draw(_:) (Swift) | drawRect: (Objective-C) |
|---|---|---|
| Signature | override func draw(_ rect: CGRect) | - (void)drawRect:(CGRect)rect |
| Appel à super | super.draw(rect) | [super drawRect:rect] |
| Contexte | UIGraphicsGetCurrentContext() | UIGraphicsGetCurrentContext() |
| Paramètre rect | rect: CGRect | CGRect rect |
| Performance | Identique | Identique |
Lors de la migration d'un projet d'Objective-C vers Swift, le renommage de la méthode est l'une des premières tâches. Xcode fournit un convertisseur automatique, mais drawRect: nécessite une mise à jour manuelle vers draw(_:). Selon Apple (2024), la version Swift draw(_:) est préférable pour les nouveaux projets.
draw(_:) s'exécute sur le CPU, et une implémentation sous-optimale peut provoquer des chutes d'images et de mauvaises performances. Apple recommande plusieurs approches éprouvées pour accélérer le dessin.
Chaque opération Core Graphics (move(to:), addLine(to:), strokePath) a un surcoût. Regroupez les opérations et utilisez CGPath pour les formes complexes — le chemin est créé une fois et réutilisé à chaque appel de draw(_:).
UIBezierPath est une enveloppe Objective-C autour de CGPath qui fournit une API simple pour créer des formes. Créez UIBezierPath à l'avance (par exemple dans l'initialiseur) et appelez simplement fill() ou stroke() à l'intérieur de draw(_:).
private let starPath: UIBezierPath = {
let path = UIBezierPath()
// Building a star shape
path.move(to: CGPoint(x: 50, y: 0))
for i in 1...5 {
let angle = CGFloat(i) * 4 * CGFloat.pi / 5
path.addLine(to: CGPoint(x: 50 + 40 * cos(angle),
y: 50 + 40 * sin(angle)))
}
path.close()
return path
}()
override func draw(_ rect: CGRect) {
UIColor.systemYellow.setFill()
starPath.fill()
}
Les développeurs commettent souvent les mêmes erreurs lors de la redéfinition de draw(_:). Connaître ces modèles permet d'éviter les bugs et la dégradation des performances. Examinons les problèmes les plus courants et leurs solutions.
En suivant ces règles, vous assurerez un dessin personnalisé stable et rapide dans tout projet iOS. Profilez draw(_:) avec Instruments (Core Animation) pour voir le temps d'exécution réel et les goulots d'étranglement.
Foire aux questions
Non, l'appel direct à draw(_:) est interdit par la documentation Apple. Le système gère lui-même le cycle de dessin. Pour demander un redessin, utilisez setNeedsDisplay(), qui marque correctement la vue comme nécessitant une mise à jour dans le cycle de rendu le plus proche.
Fonctionnellement, ces méthodes sont identiques. drawRect: est utilisé en Objective-C, draw(_:) en Swift. Les deux reçoivent un CGRect de la zone à redessiner et utilisent le même contexte Core Graphics via UIGraphicsGetCurrentContext().
Apple optimise le rendu : si une UIView n'a pas de draw(_:) redéfini, le système ne crée pas de contexte bitmap pour elle. Cela économise la mémoire. Si la redéfinition existe mais que la méthode n'est pas appelée — vérifiez que le frame de la vue n'est pas nul et que la vue est visible.
Uniquement lorsque c'est nécessaire : premier rendu, après setNeedsDisplay(), lors du changement des bounds avec contentMode = .redraw. En état statique, draw(_:) n'est pas rappelé, économisant les ressources CPU et batterie.
Apple recommande d'appeler super.draw(rect) au début de la méthode redéfinie. Bien que l'implémentation actuelle de UIView soit vide, l'appel à super garantit la compatibilité avec les futures versions d'UIKit et constitue une bonne pratique.
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