draw(_:) et drawRect — définition, appel et redéfinition

Auteur : IT Sectr Publié le : 2026-07-20 Temps de lecture : 7 min

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(_:) — méthode UIView pour le dessin personnalisé via Core Graphics, non appelée directement par le développeur
  • drawRect: — équivalent de draw(_:) en Objective-C, syntaxiquement différent, fonctionnellement identique
  • CGContext — contexte graphique disponible à l'intérieur de draw(_:) pour toutes les opérations de dessin
  • setNeedsDisplay() — la seule façon correcte de demander un redessin de draw(_:) du côté développeur
  • UIGraphicsGetCurrentContext() — fonction pour obtenir le contexte actuel dans draw(_:), étape obligatoire avant de dessiner

Qu'est-ce que draw(_:) / drawRect

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.

Signature de la méthode en Swift et Objective-C

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(_:).

objective-c
- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
    CGContextFillRect(context, rect);
}

Quand le système appelle draw(_:)

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.

  • Premier rendu — lorsque la vue est ajoutée pour la première fois à la hiérarchie et devient visible à l'écran
  • setNeedsDisplay() — après l'appel de cette méthode, le système redessine lors du prochain cycle de dessin
  • setNeedsDisplay(_:) — identique au précédent, mais en spécifiant un rectangle concret pour le redessin
  • contentMode — lorsque les bounds changent, si contentMode exige un redessin (par exemple .redraw)
  • setNeedsLayout() — dans certains cas, après la réorganisation des sous-vues, un redessin peut être nécessaire

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(_:).

Comment redéfinir draw(_:) en Swift

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.

swift
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.

La règle d'idempotence de draw(_:)

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: en Objective-C et différences avec 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éristiquedraw(_:) (Swift)drawRect: (Objective-C)
Signatureoverride func draw(_ rect: CGRect)- (void)drawRect:(CGRect)rect
Appel à supersuper.draw(rect)[super drawRect:rect]
ContexteUIGraphicsGetCurrentContext()UIGraphicsGetCurrentContext()
Paramètre rectrect: CGRectCGRect rect
PerformanceIdentiqueIdentique

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.

Optimisation du dessin dans draw(_:)

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.

Minimisez le nombre d'opérations de 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(_:).

Utilisez UIBezierPath pour les objets vectoriels

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(_:).

swift
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()
}

Erreurs courantes lors du travail avec draw(_:)

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.

  • Appel direct à draw(_:) — n'appelez jamais draw(_:) directement. Utilisez setNeedsDisplay() pour demander un redessin. L'appel direct brise le cache et peut entraîner un affichage incorrect.
  • Calculs lourds à l'intérieur de draw(_:) — draw(_:) doit être aussi léger que possible. Créez UIBezierPath, les images et autres objets lourds en dehors de la méthode, en les initialisant une seule fois.
  • Création d'objets à l'intérieur de draw(_:) — les constructions de UIColor, UIFont et UIGraphicsImageRenderer à l'intérieur de draw(_:) créent une charge mémoire inutile. Déplacez la création d'objets vers les propriétés de la classe.
  • Ignorer le paramètre rect — rect spécifie la zone qui nécessite un redessin. Dessiner en dehors de rect est ignoré par le système mais consomme des ressources. Vérifiez l'intersection avec rect avant de dessiner.
  • Absence de super.draw(rect) — bien que l'implémentation de UIView soit vide, Apple recommande d'appeler super.draw(rect) pour la compatibilité avec les futures modifications d'UIKit.

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

Peut-on appeler draw(_:) directement ?

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.

En quoi drawRect: diffère-t-il de draw(_:) ?

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().

Pourquoi draw(_:) n'est-il pas appelé pour une UIView vide ?

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.

À quelle fréquence le système appelle-t-il draw(_:) ?

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.

Faut-il appeler super.draw(rect) en Swift ?

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é

  • draw(_:) / drawRect — la méthode de dessin principale de UIView en Swift et Objective-C, appelée automatiquement par le système
  • CGContext — contexte Core Graphics disponible dans draw(_:) via UIGraphicsGetCurrentContext()
  • setNeedsDisplay() — la façon correcte de demander un redessin ; l'appel direct à draw(_:) est interdit
  • Idempotence — draw(_:) doit produire le même résultat avec les mêmes données d'entrée pour une mise en cache correcte
  • UIBezierPath — créez des chemins en dehors de draw(_:) pour minimiser la charge CPU à chaque appel
  • Paramètre rect — contient la zone de redessin ; utilisez l'intersection pour optimiser — ne dessinez pas en dehors
  • Profilage — vérifiez les performances de draw(_:) via Instruments Core Animation pour identifier les opérations lentes

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.

Discuter du projet

Lisez aussi