setNeedsDisplay(): essenza del metodo e meccanismo di ridisegno in iOS

Autore: IT Sectr Pubblicato: 2026-07-20 Tempo di lettura: 6 min

setNeedsDisplay() è un metodo di istanza di UIView che segna la vista come bisognosa di essere ridisegnata. A differenza della chiamata diretta a draw(_:), setNeedsDisplay() imposta solo il flag dirty per la vista e restituisce immediatamente il controllo. Il sistema elabora questa richiesta nel ciclo di rendering più vicino, consentendo di combinare più chiamate in un'unica operazione di disegno. Secondo la Documentazione Apple (2025), il ridisegno differito tramite setNeedsDisplay() è l'unico modo corretto per aggiornare il contenuto di una UIView personalizzata.

Punti Chiave

  • setNeedsDisplay() — una richiesta di ridisegno differito per UIView, non chiama draw(_:) in modo sincrono
  • dirty flag — un flag interno della vista impostato da setNeedsDisplay() e cancellato dopo draw(_:)
  • setNeedsDisplay(_:) — un overload con CGRect per il ridisegno parziale solo della regione specificata
  • displayIfNeeded() — ridisegno sincrono forzato, utilizzato per aggiornamenti immediati
  • Ciclo di rendering — il sistema elabora le viste dirty alla fine del run loop, combinando più richieste in un unico passaggio

Cos'è setNeedsDisplay()

setNeedsDisplay() è un metodo di UIView che segnala al sistema che il contenuto della vista è obsoleto e necessita di essere renderizzato nuovamente. Il metodo non accetta parametri e restituisce void. Dopo la chiamata, la vista viene marcata come dirty, e nel ciclo di disegno più vicino il sistema chiama draw(_:) per questa vista.

La caratteristica principale è l'asincronicità. Chiamate consecutive multiple a setNeedsDisplay() per la stessa vista non provocheranno chiamate multiple a draw(_:) — il sistema le combina in un'unica operazione. Questo è criticamente importante per le prestazioni: se una proprietà della vista cambia più volte per frame, draw(_:) viene chiamato una sola volta.

Come funziona il ridisegno differito

Il meccanismo di ridisegno differito si basa sul ciclo di rendering di UIKit. Quando viene chiamato setNeedsDisplay(), il sistema imposta il flag interno layer.needsDisplay su true. Alla fine del run loop corrente, il sistema scorre tutti i layer dirty e chiama draw(_:) per ciascuno.

Run loop e fasi di rendering

Il run loop di UIKit include una fase di aggiornamento durante la quale vengono elaborate le viste dirty. In questa fase, viene chiamato layoutSubviews() se necessario, poi draw(_:) per tutte le viste marcate. Dopo il completamento del disegno, il risultato viene passato alla GPU tramite Core Animation per il 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 }

        // Sfondo
        ctx.setFillColor(UIColor.lightGray.cgColor)
        ctx.fill(rect)

        // Barra di avanzamento
        let fillRect = CGRect(x: rect.minX, y: rect.minY,
                              width: rect.width * progress, height: rect.height)
        ctx.setFillColor(UIColor.systemGreen.cgColor)
        ctx.fill(fillRect)
    }
}

In questo esempio, progressView aggiorna progress e ogni setter chiama automaticamente setNeedsDisplay(). Il sistema attende la fine del run loop e chiama draw(_:) con il valore effettivo di progress. Se progress cambia tre volte per frame, draw(_:) viene eseguito solo una volta — con l'ultimo valore.

setNeedsDisplay() vs setNeedsDisplay(_:)

Il metodo setNeedsDisplay(_:) accetta un parametro rect di tipo CGRect, limitando l'area di ridisegno. Questo consente di ridisegnare solo la parte modificata della vista, invece dell'intera vista. La differenza di prestazioni diventa evidente quando si lavora con viste grandi dove le modifiche interessano una piccola area.

CaratteristicasetNeedsDisplay()setNeedsDisplay(_:)
ParametroNessunoCGRect — area di ridisegno
Area di ridisegnoTutti i bounds della vistaSolo il rettangolo specificato
PrestazioniInferiori per viste grandiSuperiori per modifiche parziali
Parametro rect in draw(_:)Tutti i boundsRettangolo specificato
Quando usarloModifica dell'intero contenutoModifiche puntuali (cursore, evidenziazione)

Per grafica complessa, si consiglia di disegnare prima l'intera scena in un contesto offscreen, quindi chiamare setNeedsDisplay(_:) solo per l'area modificata. Questa è una tecnica standard nelle applicazioni di disegno e negli editor grafici su iOS.

displayIfNeeded() contro setNeedsDisplay()

displayIfNeeded() è un metodo sincrono che attiva immediatamente il ridisegno di tutte le viste dirty nella gerarchia, senza attendere la fine del run loop. A differenza di setNeedsDisplay(), che imposta solo un flag, displayIfNeeded() forza l'esecuzione immediata di draw(_:).

Apple raccomanda di utilizzare displayIfNeeded() solo in casi estremi quando il risultato del disegno è necessario immediatamente — ad esempio, prima di fare uno screenshot o durante la generazione sincrona di immagini per UIGraphicsImageRenderer. Negli scenari normali, il modello asincrono tramite setNeedsDisplay() è preferibile poiché non blocca il thread principale.

swift
    // Asincrono — raccomandato
someView.setNeedsDisplay()

    // Sincrono — solo quando necessario
someView.setNeedsDisplay()
someView.displayIfNeeded() // draw(_:) chiamato immediatamente

Chiamare displayIfNeeded() senza un precedente setNeedsDisplay() non avrà effetto — il metodo elabora solo le viste già marcate come dirty. Se nessuna vista necessita di ridisegno, displayIfNeeded() termina senza chiamare draw(_:).

Ottimizzazione delle chiamate a setNeedsDisplay()

Chiamate frequenti a setNeedsDisplay() possono degradare le prestazioni, specialmente durante le animazioni. Esaminiamo le principali strategie di ottimizzazione.

Raggruppamento delle modifiche tramite un modello

Invece di chiamare setNeedsDisplay() ad ogni modifica di una proprietà individuale, accumula le modifiche e chiama il metodo una volta dopo aver applicato tutti gli aggiornamenti. Ad esempio, quando si modificano colore, dimensione e posizione di un elemento — chiama setNeedsDisplay() una volta dopo aver impostato tutte le proprietà.

Utilizzo di CALayer per proprietà animabili

Se una proprietà può essere animata tramite CALayer (backgroundColor, opacity, position, transform), usa Core Animation invece di draw(_:). Le proprietà CALayer vengono aggiornate sulla GPU senza chiamare draw(_:), fornendo 60 FPS senza carico sulla CPU.

Aggiornamento batch tramite CATransaction

Per raggruppare più modifiche in un unico passaggio di rendering, usa CATransaction. Ciò consente di combinare più chiamate setNeedsDisplay() in un'unica operazione draw(_:) alla fine della transazione.

swift
CATransaction.begin()
CATransaction.setDisableActions(true)

view1.setNeedsDisplay()
view2.setNeedsDisplay()
view3.setNeedsDisplay()

CATransaction.commit()

Non abusare di displayIfNeeded() — il ridisegno sincrono blocca il thread principale e può causare cadute di fotogrammi. Usalo solo per operazioni in cui il risultato del disegno è critico prima della fine del run loop corrente.

Domande Frequenti

Posso chiamare draw(_:) direttamente invece di setNeedsDisplay()?

No, chiamare direttamente draw(_:) è vietato. setNeedsDisplay() marca correttamente la vista come dirty e il sistema chiama draw(_:) nel momento appropriato del ciclo di rendering. Chiamare direttamente draw(_:) ignora il meccanismo di caching e può portare a uno stato incoerente.

Cosa succede con chiamate multiple a setNeedsDisplay()?

Chiamate multiple a setNeedsDisplay() per la stessa vista vengono combinate dal sistema in un'unica chiamata a draw(_:). Questo accade perché il metodo imposta solo il dirty flag, che viene cancellato solo dopo l'esecuzione effettiva di draw(_:) alla fine del run loop.

In cosa si differenzia setNeedsDisplay() da setNeedsLayout()?

setNeedsDisplay() richiede un ridisegno del contenuto della vista (chiamata a draw(_:)). setNeedsLayout() richiede una riorganizzazione delle subview (chiamata a layoutSubviews()). layoutSubviews() può portare a cambiamenti di dimensione, che a loro volta possono innescare un ridisegno.

Quando usare setNeedsDisplay(_:)?

setNeedsDisplay(_:) dovrebbe essere usato per modifiche puntuali: movimento del cursore, cambio di colore di un'area selezionata, aggiornamento di parte di un grafico. Questo limita l'area di ridisegno e migliora le prestazioni rispetto al ridisegno dell'intera vista.

Cambiare il frame chiama automaticamente setNeedsDisplay()?

Per impostazione predefinita, cambiare il frame o i bounds non chiama setNeedsDisplay(). Il sistema sposta semplicemente la vista. Se il ridisegno è necessario quando le dimensioni cambiano, imposta la proprietà contentMode su .redraw — allora UIKit chiamerà automaticamente setNeedsDisplay() ad ogni cambio di bounds.

Riepilogo

  • setNeedsDisplay() — un metodo asincrono di UIView per richiedere il ridisegno differito impostando il dirty flag
  • setNeedsDisplay(_:) — un overload con CGRect per il ridisegno parziale solo della regione specificata, ottimizzando le prestazioni
  • displayIfNeeded() — un metodo sincrono per il ridisegno immediato delle viste dirty, usato solo quando assolutamente necessario
  • Run loop — il sistema elabora le viste dirty alla fine del ciclo corrente, combinando più chiamate in un unico passaggio draw(_:)
  • CALayer — per le proprietà animabili (position, opacity) usa Core Animation invece di draw(_:) — è enormemente più efficiente
  • CATransaction — raggruppa più modifiche in un ciclo di rendering, riducendo il numero di chiamate a draw(_:)
  • contentMode = .redraw — abilita la chiamata automatica a setNeedsDisplay() quando cambiano i bounds della vista

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche