draw(_:) e drawRect — cosa sono, chiamata e override

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

draw(_:) / drawRect è un metodo della classe UIView (in Swift) e il suo equivalente in Objective-C (drawRect:), responsabile del rendering del contenuto della vista utilizzando Core Graphics. Il sistema chiama automaticamente questo metodo quando la vista appare per la prima volta sullo schermo e dopo la chiamata a setNeedsDisplay(). Secondo la Documentazione Apple (2025), draw(_:) è l'unico metodo in cui lo sviluppatore ha accesso al contesto grafico dello schermo corrente per il disegno personalizzato. L'override di draw(_:) dà il controllo completo sull'aspetto del componente — dalle semplici forme geometriche alla grafica animata complessa.

Punti chiave

  • draw(_:) — metodo UIView per disegno personalizzato tramite Core Graphics, non chiamato direttamente dallo sviluppatore
  • drawRect: — equivalente di draw(_:) in Objective-C, sintatticamente diverso, funzionalmente identico
  • CGContext — contesto grafico disponibile all'interno di draw(_:) per tutte le operazioni di disegno
  • setNeedsDisplay() — l'unico modo corretto per richiedere un ridisegno di draw(_:) da parte dello sviluppatore
  • UIGraphicsGetCurrentContext() — funzione per ottenere il contesto corrente all'interno di draw(_:), passaggio obbligatorio prima di disegnare

Cos'è draw(_:) / drawRect

draw(_:) è un metodo di istanza di UIView che UIKit chiama per renderizzare il contenuto della vista. All'interno di questo metodo, lo sviluppatore ottiene l'accesso al contesto grafico CGContext e utilizza l'API di Core Graphics per disegnare linee, riempimenti, testo e immagini. drawRect: in Objective-C esegue la stessa funzione ma con sintassi diversa: l'unico parametro è un CGRect che specifica l'area da ridisegnare.

Secondo Apple Engineering (2024), draw(_:) funziona tramite rendering CPU su Bitmap Graphics Context, che offre la massima flessibilità ma richiede più risorse rispetto a CALayer. La decisione di utilizzare draw(_:) viene presa in base alla complessità della grafica e ai requisiti di prestazioni.

Firma del metodo in Swift e Objective-C

In Swift, il metodo viene dichiarato come override func draw(_ rect: CGRect), dove rect è il rettangolo che deve essere ridisegnato. In Objective-C, la firma è - (void)drawRect:(CGRect)rect. Il parametro rect può essere più piccolo dei bounds della vista durante il ridisegno parziale tramite setNeedsDisplay(_:).

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

Quando il sistema chiama draw(_:)

Il sistema chiama automaticamente draw(_:) in scenari strettamente definiti. Comprendere questi trigger aiuta a evitare ridisegni non necessari e ottimizzare le prestazioni della vista. Di seguito sono elencati tutti i casi di invocazione automatica del metodo.

  • Primo rendering — quando la vista viene aggiunta per la prima volta alla gerarchia e diventa visibile sullo schermo
  • setNeedsDisplay() — dopo la chiamata a questo metodo, il sistema ridisegna al prossimo ciclo di disegno
  • setNeedsDisplay(_:) — come sopra, ma specificando un rettangolo concreto per il ridisegno
  • contentMode — quando i bounds cambiano, se contentMode richiede il ridisegno (ad esempio .redraw)
  • setNeedsLayout() — in alcuni casi, dopo la riorganizzazione delle subview, potrebbe essere necessario il ridisegno

La documentazione Apple avverte: non chiamare mai draw(_:) direttamente. Il sistema decide da solo quando eseguire il disegno, e una chiamata diretta interrompe il meccanismo interno di cache. Per richiedere un ridisegno, utilizzare sempre setNeedsDisplay() o setNeedsDisplay(_:).

Come fare l'override di draw(_:) in Swift

L'override di draw(_:) in Swift inizia con l'ottenimento del contesto grafico e le successive chiamate a Core Graphics. Si consiglia di creare metodi separati per blocchi logici di disegno — questo migliora la leggibilità e la testabilità del codice.

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

In questo esempio, draw(_:) disegna un triangolo con un contorno grigio scuro spesso 2 pixel. La chiamata a super.draw(rect) all'inizio del metodo è raccomandata da Apple per preservare la logica di disegno del genitore, sebbene l'implementazione predefinita di draw(_:) in UIView sia vuota.

La regola di idempotenza di draw(_:)

Ogni chiamata a draw(_:) deve produrre un risultato identico con gli stessi dati di input. Ciò consente al sistema di memorizzare nella cache il risultato e di non chiamare draw(_:) nuovamente se il contenuto della vista non è cambiato. Non utilizzare valori casuali, ora di sistema o richieste di rete all'interno di draw(_:).

drawRect: in Objective-C e differenze da draw(_:)

drawRect: è storicamente la prima versione del metodo, apparsa in iOS 2.0 con Objective-C. In Swift, il metodo è stato rinominato in draw(_:) utilizzando il parametro esterno _. Funzionalmente, i metodi sono identici: entrambi ricevono un CGRect dell'area da ridisegnare e utilizzano UIGraphicsGetCurrentContext() per accedere al contesto grafico.

Caratteristicadraw(_:) (Swift)drawRect: (Objective-C)
Firmaoverride func draw(_ rect: CGRect)- (void)drawRect:(CGRect)rect
Chiamata a supersuper.draw(rect)[super drawRect:rect]
ContestoUIGraphicsGetCurrentContext()UIGraphicsGetCurrentContext()
Parametro rectrect: CGRectCGRect rect
PrestazioniIdenticheIdentiche

Durante la migrazione di un progetto da Objective-C a Swift, la ridenominazione del metodo è uno dei primi compiti. Xcode fornisce un convertitore automatico, ma drawRect: richiede un aggiornamento manuale a draw(_:). Secondo Apple (2024), la versione Swift draw(_:) è preferibile per i nuovi progetti.

Ottimizzazione del disegno in draw(_:)

draw(_:) viene eseguito sulla CPU e un'implementazione non ottimale può causare cadute di fotogrammi e scarse prestazioni. Apple raccomanda diversi approcci comprovati per accelerare il disegno.

Minimizzare il numero di operazioni di disegno

Ogni operazione di Core Graphics (move(to:), addLine(to:), strokePath) ha un overhead. Raggruppare le operazioni e utilizzare CGPath per forme complesse — il percorso viene creato una volta e riutilizzato ad ogni chiamata di draw(_:).

Utilizzare UIBezierPath per oggetti vettoriali

UIBezierPath è un wrapper Objective-C di CGPath che fornisce una semplice API per creare forme. Creare UIBezierPath in anticipo (ad esempio nell'inizializzatore) e chiamare semplicemente fill() o stroke() all'interno di 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()
}

Errori comuni quando si lavora con draw(_:)

Gli sviluppatori commettono spesso gli stessi errori quando fanno l'override di draw(_:). Conoscere questi modelli aiuta a evitare bug e degrado delle prestazioni. Vediamo i problemi più comuni e le loro soluzioni.

  • Chiamata diretta a draw(_:) — non chiamare mai draw(_:) direttamente. Utilizzare setNeedsDisplay() per richiedere un ridisegno. La chiamata diretta rompe la cache e può portare a una visualizzazione errata.
  • Calcoli pesanti all'interno di draw(_:) — draw(_:) dovrebbe essere il più leggero possibile. Creare UIBezierPath, immagini e altri oggetti pesanti al di fuori del metodo, inizializzandoli una sola volta.
  • Creazione di oggetti all'interno di draw(_:) — le costruzioni di UIColor, UIFont e UIGraphicsImageRenderer all'interno di draw(_:) creano un carico di memoria non necessario. Spostare la creazione di oggetti nelle proprietà della classe.
  • Ignorare il parametro rect — rect specifica l'area che necessita di ridisegno. Disegnare fuori da rect viene scartato dal sistema ma consuma risorse. Verificare l'intersezione con rect prima di disegnare.
  • Mancanza di super.draw(rect) — sebbene l'implementazione di UIView sia vuota, Apple raccomanda di chiamare super.draw(rect) per compatibilità con future modifiche di UIKit.

Seguendo queste regole, garantirai un disegno personalizzato stabile e veloce in qualsiasi progetto iOS. Profila draw(_:) con Instruments (Core Animation) per vedere il tempo di esecuzione effettivo e i colli di bottiglia.

Domande frequenti

Si può chiamare draw(_:) direttamente?

No, la chiamata diretta a draw(_:) è vietata dalla documentazione Apple. Il sistema gestisce da solo il ciclo di disegno. Per richiedere un ridisegno, utilizzare setNeedsDisplay(), che contrassegna correttamente la vista come bisognosa di aggiornamento nel ciclo di rendering più vicino.

In che cosa drawRect: differisce da draw(_:)?

Funzionalmente questi metodi sono identici. drawRect: viene utilizzato in Objective-C, draw(_:) in Swift. Entrambi ricevono un CGRect dell'area da ridisegnare e utilizzano lo stesso contesto Core Graphics tramite UIGraphicsGetCurrentContext().

Perché draw(_:) non viene chiamato per una UIView vuota?

Apple ottimizza il rendering: se una UIView non ha un draw(_:) sovrascritto, il sistema non crea un contesto bitmap per essa. Questo risparmia memoria. Se l'override esiste ma il metodo non viene chiamato — verificare che il frame della vista non sia zero e che la vista sia visibile.

Con quale frequenza il sistema chiama draw(_:)?

Solo quando necessario: primo rendering, dopo setNeedsDisplay(), quando i bounds cambiano con contentMode = .redraw. In stato statico, draw(_:) non viene richiamato, risparmiando risorse CPU e batteria.

È necessario chiamare super.draw(rect) in Swift?

Apple raccomanda di chiamare super.draw(rect) all'inizio del metodo sovrascritto. Sebbene l'implementazione corrente di UIView sia vuota, la chiamata a super garantisce la compatibilità con future versioni di UIKit ed è una buona pratica.

Riepilogo

  • draw(_:) / drawRect — il principale metodo di disegno di UIView in Swift e Objective-C, chiamato automaticamente dal sistema
  • CGContext — contesto Core Graphics disponibile in draw(_:) tramite UIGraphicsGetCurrentContext()
  • setNeedsDisplay() — il modo corretto per richiedere un ridisegno; la chiamata diretta a draw(_:) è vietata
  • Idempotenza — draw(_:) deve produrre lo stesso risultato con gli stessi dati di input per una corretta cache
  • UIBezierPath — creare percorsi al di fuori di draw(_:) per minimizzare il carico CPU ad ogni chiamata
  • Parametro rect — contiene l'area di ridisegno; utilizzare l'intersezione per ottimizzare — non disegnare al di fuori
  • Profilatura — verificare le prestazioni di draw(_:) tramite Instruments Core Animation per identificare operazioni lente

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