setNeedsDisplay() est une méthode d'instance de UIView qui marque la vue comme devant être redessinée. Contrairement à l'appel direct de draw(_:), setNeedsDisplay() définit seulement le flag dirty pour la vue et rend le contrôle immédiatement. Le système traite cette demande dans le cycle de rendu le plus proche, permettant de combiner plusieurs appels en une seule opération de dessin. Selon la Documentation Apple (2025), le redessin différé via setNeedsDisplay() est la seule façon correcte de mettre à jour le contenu d'une UIView personnalisée.
Points Clés
setNeedsDisplay() est une méthode de UIView qui signale au système que le contenu de la vue est obsolète et nécessite un nouveau rendu. La méthode ne prend aucun paramètre et retourne void. Après l'appel, la vue est marquée comme dirty, et dans le cycle de dessin le plus proche, le système appelle draw(_:) pour cette vue.
La caractéristique clé est l'asynchronisme. Plusieurs appels consécutifs à setNeedsDisplay() pour la même vue n'entraîneront pas de multiples appels à draw(_:) — le système les combine en une seule opération. Ceci est d'une importance critique pour les performances : si une propriété de vue change plusieurs fois par image, draw(_:) n'est appelé qu'une seule fois.
Le mécanisme de redessin différé est basé sur le cycle de rendu UIKit. Lorsque setNeedsDisplay() est appelé, le système définit le flag interne layer.needsDisplay sur true. À la fin du run loop actuel, le système parcourt toutes les couches dirty et appelle draw(_:) pour chacune.
Le run loop UIKit comprend une phase de mise à jour pendant laquelle les vues dirty sont traitées. Dans cette phase, layoutSubviews() est appelé si nécessaire, puis draw(_:) pour toutes les vues marquées. Après la fin du dessin, le résultat est transmis au GPU via Core Animation pour le compositing.
class ProgressView: UIView {
var progress: CGFloat = 0 {
didSet {
setNeedsDisplay()
}
}
override func draw(_ rect: CGRect) {
super.draw(rect)
guard let ctx = UIGraphicsGetCurrentContext() else { return }
// Arrière-plan
ctx.setFillColor(UIColor.lightGray.cgColor)
ctx.fill(rect)
// Barre de progression
let fillRect = CGRect(x: rect.minX, y: rect.minY,
width: rect.width * progress, height: rect.height)
ctx.setFillColor(UIColor.systemGreen.cgColor)
ctx.fill(fillRect)
}
}
Dans cet exemple, progressView met à jour progress, et chaque setter appelle automatiquement setNeedsDisplay(). Le système attend la fin du run loop et appelle draw(_:) avec la valeur réelle de progress. Si progress change trois fois par image, draw(_:) ne s'exécute qu'une seule fois — avec la dernière valeur.
La méthode setNeedsDisplay(_:) prend un paramètre rect de type CGRect, limitant la zone de redessin. Cela permet de redessiner uniquement la partie modifiée de la vue, plutôt que la vue entière. La différence de performances devient notable lorsqu'on travaille avec de grandes vues où les changements affectent une petite zone.
| Caractéristique | setNeedsDisplay() | setNeedsDisplay(_:) |
|---|---|---|
| Paramètre | Aucun | CGRect — zone de redessin |
| Zone de redessin | Tous les bounds de la vue | Uniquement le rectangle spécifié |
| Performances | Inférieures pour les grandes vues | Supérieures pour les changements partiels |
| Paramètre rect dans draw(_:) | Tous les bounds | Rectangle spécifié |
| Quand l'utiliser | Changement du contenu entier | Modifications ponctuelles (curseur, surbrillance) |
Pour les graphiques complexes, il est recommandé de dessiner d'abord toute la scène dans un contexte hors écran, puis d'appeler setNeedsDisplay(_:) uniquement pour la zone modifiée. C'est une technique standard dans les applications de dessin et les éditeurs graphiques sur iOS.
displayIfNeeded() est une méthode synchrone qui déclenche immédiatement le redessin de toutes les vues dirty dans la hiérarchie, sans attendre la fin du run loop. Contrairement à setNeedsDisplay(), qui ne fait que définir un flag, displayIfNeeded() force l'exécution immédiate de draw(_:).
Apple recommande d'utiliser displayIfNeeded() uniquement dans les cas extrêmes où le résultat du dessin est nécessaire immédiatement — par exemple, avant de prendre une capture d'écran ou lors de la génération synchrone d'images pour UIGraphicsImageRenderer. Dans les scénarios normaux, le modèle asynchrone via setNeedsDisplay() est préférable car il ne bloque pas le thread principal.
// Asynchrone — recommandé
someView.setNeedsDisplay()
// Synchrone — seulement si nécessaire
someView.setNeedsDisplay()
someView.displayIfNeeded() // draw(_:) appelé immédiatement
Appeler displayIfNeeded() sans setNeedsDisplay() préalable n'aura aucun effet — la méthode ne traite que les vues déjà marquées comme dirty. Si aucune vue ne nécessite de redessin, displayIfNeeded() se termine sans appeler draw(_:).
Des appels fréquents à setNeedsDisplay() peuvent dégrader les performances, surtout pendant les animations. Examinons les principales stratégies d'optimisation.
Au lieu d'appeler setNeedsDisplay() à chaque modification d'une propriété individuelle, accumulez les changements et appelez la méthode une fois après avoir appliqué toutes les mises à jour. Par exemple, lors du changement de couleur, taille et position d'un élément — appelez setNeedsDisplay() une fois après avoir défini toutes les propriétés.
Si une propriété peut être animée via CALayer (backgroundColor, opacity, position, transform), utilisez Core Animation plutôt que draw(_:). Les propriétés CALayer sont mises à jour sur le GPU sans appeler draw(_:), offrant 60 FPS sans charge CPU.
Pour regrouper plusieurs changements en un seul passage de rendu, utilisez CATransaction. Cela permet de combiner plusieurs appels setNeedsDisplay() en une seule opération draw(_:) à la fin de la transaction.
CATransaction.begin()
CATransaction.setDisableActions(true)
view1.setNeedsDisplay()
view2.setNeedsDisplay()
view3.setNeedsDisplay()
CATransaction.commit()
N'abusez pas de displayIfNeeded() — le redessin synchrone bloque le thread principal et peut provoquer des chutes d'images. Utilisez-le uniquement pour les opérations où le résultat du dessin est critique avant la fin du run loop actuel.
Foire Aux Questions
Non, l'appel direct à draw(_:) est interdit. setNeedsDisplay() marque correctement la vue comme dirty, et le système appelle draw(_:) au moment opportun du cycle de rendu. L'appel direct à draw(_:) ignore le mécanisme de cache et peut entraîner un état incohérent.
Plusieurs appels à setNeedsDisplay() pour la même vue sont combinés par le système en un seul appel à draw(_:). Cela se produit car la méthode ne fait que définir le dirty flag, qui n'est effacé qu'après l'exécution réelle de draw(_:) à la fin du run loop.
setNeedsDisplay() demande un redessin du contenu de la vue (appel à draw(_:)). setNeedsLayout() demande une réorganisation des sous-vues (appel à layoutSubviews()). layoutSubviews() peut entraîner des changements de taille, qui à leur tour peuvent déclencher un redessin.
setNeedsDisplay(_:) doit être utilisé pour les modifications ponctuelles : mouvement du curseur, changement de couleur d'une zone sélectionnée, mise à jour d'une partie d'un graphique. Cela limite la zone de redessin et améliore les performances par rapport au redessin de la vue entière.
Par défaut, changer le frame ou les bounds n'appelle pas setNeedsDisplay(). Le système déplace simplement la vue. Si un redessin est nécessaire lors du changement de taille, définissez la propriété contentMode sur .redraw — alors UIKit appellera automatiquement setNeedsDisplay() à chaque changement de bounds.
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