setNeedsDisplay() : essence de la méthode et mécanisme de redessin dans iOS

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

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() — une demande de redessin différé pour UIView, n'appelle pas draw(_:) de manière synchrone
  • dirty flag — un flag interne de la vue défini par setNeedsDisplay() et effacé après draw(_:)
  • setNeedsDisplay(_:) — une surcharge avec CGRect pour le redessin partiel uniquement de la région spécifiée
  • displayIfNeeded() — redessin synchrone forcé, utilisé pour les mises à jour immédiates
  • Cycle de rendu — le système traite les vues dirty à la fin du run loop, combinant plusieurs demandes en un seul passage

Qu'est-ce que setNeedsDisplay()

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.

Comment fonctionne le redessin différé

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.

Run loop et étapes de rendu

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.

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

setNeedsDisplay() vs setNeedsDisplay(_:)

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éristiquesetNeedsDisplay()setNeedsDisplay(_:)
ParamètreAucunCGRect — zone de redessin
Zone de redessinTous les bounds de la vueUniquement le rectangle spécifié
PerformancesInférieures pour les grandes vuesSupérieures pour les changements partiels
Paramètre rect dans draw(_:)Tous les boundsRectangle spécifié
Quand l'utiliserChangement du contenu entierModifications 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() contre setNeedsDisplay()

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.

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

Optimisation des appels à setNeedsDisplay()

Des appels fréquents à setNeedsDisplay() peuvent dégrader les performances, surtout pendant les animations. Examinons les principales stratégies d'optimisation.

Regroupement des changements via un modèle

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.

Utilisation de CALayer pour les propriétés animables

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.

Mise à jour par lot via CATransaction

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.

swift
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

Puis-je appeler draw(_:) directement au lieu de setNeedsDisplay() ?

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.

Que se passe-t-il avec plusieurs appels à setNeedsDisplay() ?

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.

En quoi setNeedsDisplay() diffère-t-il de setNeedsLayout() ?

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.

Quand utiliser setNeedsDisplay(_:) ?

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.

Est-ce que changer le frame appelle automatiquement setNeedsDisplay() ?

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é

  • setNeedsDisplay() — une méthode asynchrone de UIView pour demander un redessin différé en définissant le dirty flag
  • setNeedsDisplay(_:) — une surcharge avec CGRect pour le redessin partiel uniquement de la région spécifiée, optimisant les performances
  • displayIfNeeded() — une méthode synchrone pour le redessin immédiat des vues dirty, utilisée seulement en cas d'absolue nécessité
  • Run loop — le système traite les vues dirty à la fin du cycle actuel, combinant plusieurs appels en un seul passage draw(_:)
  • CALayer — pour les propriétés animables (position, opacity), utilisez Core Animation au lieu de draw(_:) — c'est infiniment plus performant
  • CATransaction — regroupe plusieurs changements en un cycle de rendu, réduisant le nombre d'appels à draw(_:)
  • contentMode = .redraw — active l'appel automatique à setNeedsDisplay() lors du changement des bounds de la vue

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