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(_:) è 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.
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(_:).
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
CGContextFillRect(context, rect);
}
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.
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(_:).
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.
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.
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: è 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.
| Caratteristica | draw(_:) (Swift) | drawRect: (Objective-C) |
|---|---|---|
| Firma | override func draw(_ rect: CGRect) | - (void)drawRect:(CGRect)rect |
| Chiamata a super | super.draw(rect) | [super drawRect:rect] |
| Contesto | UIGraphicsGetCurrentContext() | UIGraphicsGetCurrentContext() |
| Parametro rect | rect: CGRect | CGRect rect |
| Prestazioni | Identiche | Identiche |
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.
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.
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(_:).
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(_:).
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()
}
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.
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
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.
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().
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.
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.
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
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.
Leggi anche