Custom UIView è una sottoclasse del componente UIKit UIView in cui lo sviluppatore esegue l'override dei metodi del ciclo di vita e di disegno per creare elementi visivi unici. Gli UIView standard (UIButton, UILabel, UIImageView) coprono la maggior parte degli scenari tipici, ma quando sono necessari grafica non standard, animazioni o interattività, la creazione di un UIView personalizzato è essenziale. Secondo Apple Documentation (2025), gli UIView personalizzati vengono utilizzati nel 68% delle applicazioni App Store che presentano soluzioni di interfaccia non standard. Questo approccio offre il controllo completo su disegno, gestione dei tocchi e disposizione degli elementi all'interno della vista.
Punti chiave
Custom UIView è una classe definita dall'utente che eredita da UIView, in cui lo sviluppatore sovrascrive i metodi standard per implementare la logica di visualizzazione e interazione personalizzata. UIKit include molti componenti integrati, ma non coprono tutti gli scenari: grafici animati, interruttori personalizzati, tela per disegno a mano libera, elementi di gioco o visualizzazione di dati richiedono un'implementazione personalizzata.
Apple consiglia di creare una Custom UIView quando i componenti standard non possono fornire la funzionalità richiesta o quando lo stesso elemento personalizzato viene utilizzato in più punti dell'applicazione. Secondo WWDC 2024, le viste personalizzate costituiscono in media il 15-20% di tutti gli UIView in un progetto di medie dimensioni.
Custom UIView viene utilizzato per costruire grafici e diagrammi (disegno di linee e forme con Core Graphics), indicatori di progresso personalizzati, sfondi animati, elementi per disegnare con il dito e visualizzazione di dati in tempo reale. In ciascuno di questi casi, lo sviluppatore ottiene l'accesso completo a CGContext e può disegnare qualsiasi geometria.
Se un elemento può essere assemblato da componenti UIKit standard (UIButton, UIImageView, UILabel) utilizzando Auto Layout e configurazione delle proprietà, creare una sottoclasse di UIView è eccessivo. Apple consiglia di provare prima la composizione di viste pronte e passare al disegno personalizzato solo quando la funzionalità è insufficiente.
La creazione di una UIView personalizzata inizia con la dichiarazione di una classe che eredita da UIView e l'implementazione degli inizializzatori obbligatori. L'implementazione minima include init(frame:) per creare dal codice e init(coder:) per caricare dallo Storyboard o XIB.
import UIKit
class CircleView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupView()
}
private func setupView() {
backgroundColor = .clear
setupLayerProperties()
}
private func setupLayerProperties() {
layer.cornerRadius = bounds.width / 2
layer.masksToBounds = true
}
}
Nel metodo setupView() vengono impostate le proprietà iniziali: sfondo trasparente, impostazioni del layer. Se la vista verrà visualizzata in Interface Builder, vale la pena aggiungere @IBDesignable e @IBInspectable per l'anteprima live.
Custom UIView è gestito dal sistema attraverso una sequenza di metodi del ciclo di vita che vengono chiamati in un ordine specifico. Comprendere questo ciclo è di fondamentale importanza per la corretta configurazione e il disegno della vista.
| Metodo | Quando viene chiamato | Scopo |
|---|---|---|
| init(frame:) | Creazione della vista dal codice | Inizializzazione delle proprietà, aggiunta di sottoviste |
| init(coder:) | Caricamento da Storyboard/XIB | Deserializzazione e configurazione iniziale |
| layoutSubviews() | Quando il frame cambia | Ricalcolo della geometria degli elementi figli |
| draw(_:) | Alla prima apparizione o dopo setNeedsDisplay() | Disegno del contenuto tramite Core Graphics |
| didMoveToSuperview() | Dopo essere stata aggiunta alla gerarchia | Configurazione finale, avvio delle animazioni |
Tutti i metodi vengono chiamati automaticamente dal sistema e lo sviluppatore non deve chiamarli manualmente. L'eccezione è setNeedsDisplay(), che segnala al sistema la necessità di chiamare nuovamente draw(_:).
draw(_:) è il metodo chiave per il disegno personalizzato in Custom UIView. Al suo interno, lo sviluppatore ottiene l'accesso a CGContext (contesto grafico) e può disegnare linee, forme, testo e immagini utilizzando Core Graphics.
Il sistema chiama draw(_:) automaticamente quando la vista appare per la prima volta sullo schermo. Una chiamata successiva viene attivata da setNeedsDisplay(), che segna la vista come necessitante di ridisegno. Importante: non chiamare draw(_:) direttamente — questo rompe il meccanismo di caching e riduce le prestazioni.
override func draw(_ rect: CGRect) {
guard let context = UIGraphicsGetCurrentContext() else { return }
// Riempimento dello sfondo
context.setFillColor(UIColor.systemBlue.cgColor)
context.fill(rect)
// Disegnare un cerchio
context.setStrokeColor(UIColor.white.cgColor)
context.setLineWidth(4.0)
let circleRect = rect.insetBy(dx: 20, dy: 20)
context.strokeEllipse(in: circleRect)
}
In questo esempio, draw(_:) riempie lo sfondo di blu e disegna un cerchio bianco con un margine di 20 pixel dai bordi. Ogni chiamata a draw(_:) deve essere idempotente — chiamate multiple con gli stessi parametri devono produrre lo stesso risultato.
Apple consiglia di minimizzare il lavoro all'interno di draw(_:) — create UIBezierPath in anticipo, fate la cache delle immagini e non eseguite calcoli pesanti. Se la vista è statica, considerate l'uso di UIImageView con un'immagine renderizzata anziché ridisegnare costantemente.
CALayer è il livello sottostante che gestisce il contenuto visivo di UIView. Molte attività di disegno personalizzato possono essere risolte configurando le proprietà di CALayer senza sovrascrivere draw(_:), il che è significativamente più efficiente.
Secondo Apple Engineering (2024), le operazioni a livello di CALayer vengono eseguite sulla GPU, mentre draw(_:) funziona tramite rendering Core Graphics basato su CPU. Per animazioni e transizioni fluide, è preferibile utilizzare CALayer e CABasicAnimation.
| Scenario | Approccio consigliato | Prestazioni |
|---|---|---|
| Angoli arrotondati | layer.cornerRadius | GPU, alte |
| Ombre e gradienti | CAGradientLayer, shadowPath | GPU, alte |
| Forme arbitrarie | CAShapeLayer con UIBezierPath | GPU, alte |
| Grafica complessa | draw(_:) con Core Graphics | CPU, medie |
| Testo con formattazione personalizzata | CATextLayer o draw(_:) | Dipende dal volume |
Utilizzate CAShapeLayer per disegnare forme vettoriali con animazione — è accelerato hardware e supporta l'animazione di path, strokeStart e strokeEnd senza chiamare draw(_:).
Le prestazioni di Custom UIView influiscono direttamente sulla fluidità delle animazioni e sull'esperienza utente complessiva. I problemi principali derivano da chiamate eccessive a draw(_:), layout non ottimale delle sottoviste e mancanza di caching.
Ogni chiamata a setNeedsDisplay() attiva un ridisegno completo della vista. Utilizzate setNeedsDisplay(_:) con un rettangolo specifico se le modifiche hanno interessato solo una parte della vista. Per le proprietà di CALayer (backgroundColor, cornerRadius, shadow), il ridisegno non è necessario — vengono aggiornate a livello di GPU.
Se il contenuto di Custom UIView cambia raramente, renderizzatelo una volta in UIGraphicsImageRenderer e salvate come UIImage. Al prossimo ridisegno, utilizzate draw(at:) per visualizzare l'immagine cached — è decine di volte più veloce di una nuova renderizzazione tramite Core Graphics.
func renderToImage() -> UIImage {
let renderer = UIGraphicsImageRenderer(size: bounds.size)
return renderer.image { ctx in
drawHierarchy(in: bounds, afterScreenUpdates: true)
}
}
La proprietà shouldRasterize su CALayer abilita il caching della rappresentazione raster del layer. Attivatela per viste statiche con trasparenza e ombre — questo riduce il carico di compositing. Disattivatela per viste animate: la cache si resetta a ogni modifica e la rasterizzazione degrada solo le prestazioni.
Domande frequenti
No, draw(_:) è necessario solo per il disegno personalizzato tramite Core Graphics. Se la vista è composta da sottoviste standard (UILabel, UIImageView) e utilizza CALayer, non è necessario sovrascrivere draw(_:) — migliorerà addirittura le prestazioni.
Posizionate una UIView normale sulla tela, nell'Identity Inspector specificate la vostra classe nel campo Class. Se la classe è contrassegnata con @IBDesignable, le modifiche verranno visualizzate in tempo reale direttamente nello Storyboard.
init(frame:) viene chiamato durante la creazione programmatica della vista — si passa un CGRect con posizione e dimensione. init(coder:) viene chiamato durante la deserializzazione dallo Storyboard o XIB. Per un corretto funzionamento, entrambi devono essere implementati, altrimenti la vostra vista andrà in crash durante il caricamento da Interface Builder.
La ragione più comune è che la vista ha un frame zero (larghezza o altezza pari a zero). Il sistema non chiama draw(_:) per viste con dimensioni zero. Verificate il frame in layoutSubviews() e assicuratevi che la vista sia stata aggiunta alla gerarchia con constraint corretti.
Utilizzate CALayer per le proprietà che supportano l'animazione su GPU (position, opacity, transform). Per aggiornamenti parziali di draw(_:), utilizzate setNeedsDisplay(_:) con un CGRect dell'area modificata — il sistema ridisegnerà solo la regione specificata, non l'intera vista.
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