@ViewBuilder è un'annotazione result builder in SwiftUI progettata per la costruzione dichiarativa di gerarchie di View. Secondo Apple Developer Documentation, 2024, @ViewBuilder trasforma un blocco di codice con multiple espressioni e logica condizionale in un unico tipo View comprensibile al compilatore Swift. Senza questa annotazione, sarebbe impossibile utilizzare la familiare sintassi dichiarativa di SwiftUI con if/else e molteplici elementi nel body.
Punti chiave
@ViewBuilder è un'annotazione che implementa il pattern result builder (SE-0289), che consente a SwiftUI di comporre più Views in un'unica composizione usando la sintassi dichiarativa. Avvolge automaticamente espressioni multiple, costrutti condizionali e valori opzionali nei loro tipi corrispondenti: TupleView, ConditionalContent, OptionalContent.
Prima dell'arrivo dei result builder, gli sviluppatori dovevano avvolgere manualmente gli elementi in VStack o HStack e usare operatori ternari o metodi factory per la logica condizionale. @ViewBuilder ha reso la sintassi di SwiftUI concisa e leggibile, permettendo di scrivere codice che assomiglia a Swift normale con if/else e cicli.
Secondo Swift Evolution SE-0289, i result builder sono un meccanismo generale non legato a SwiftUI. @ViewBuilder è un'implementazione di questo meccanismo, insieme a @StringBuilder per la costruzione di stringhe e implementazioni libreria per altri DSL. In SwiftUI, @ViewBuilder è utilizzato non solo per body, ma anche per i parametri closure dei contenitori (VStack, HStack, ZStack, List).
Nell'UIKit imperativo, crei esplicitamente un UIView, configuri le sue proprietà e lo aggiungi alla gerarchia tramite addSubview. In SwiftUI con @ViewBuilder, descrivi dichiarativamente quali Views devono essere visualizzate, e SwiftUI gestisce la creazione, l'aggiornamento e la rimozione degli elementi in base ai cambiamenti di stato.
Result builder è un meccanismo di Swift che trasforma una sequenza di espressioni in un unico valore composito attraverso i metodi statici buildBlock, buildOptional, buildEither e altri. Quando il compilatore vede l'annotazione @ViewBuilder, applica automaticamente questi metodi al blocco di codice durante la compilazione.
@resultBuilder
struct ViewBuilder {
static func buildBlock<C0, C1>(_ c0: C0, _ c1: C1) -> TupleView<(C0, C1)>
static func buildIf<C>(_ c: C?) -> C?
static func buildEither<T, F>(first: T) -> ConditionalContent<T, F>
static func buildEither<T, F>(second: F) -> ConditionalContent<T, F>
}
buildBlock accetta da 1 a 10 espressioni e restituisce un TupleView. Ogni arità (numero di espressioni) ha il proprio overload di buildBlock: da buildBlock
buildEither (first/second) gestisce i costrutti if/else. Ogni ramo viene passato al metodo corrispondente e il risultato viene avvolto in ConditionalContent — un tipo che nasconde i tipi specifici dei rami e fornisce un'interfaccia unificata per SwiftUI.
In SwiftUI, la proprietà body è già implicitamente annotata con @ViewBuilder — non vedi questa annotazione nel codice, ma il compilatore la applica automaticamente. Tuttavia, per proprietà personalizzate che restituiscono più Views, o per parametri closure, l'annotazione deve essere specificata esplicitamente.
Limitazione 1 — 10 elementi in un blocco. Questa è la limitazione più nota di @ViewBuilder. Se devi visualizzare più di 10 elementi allo stesso livello, il compilatore emetterà un errore. Le soluzioni includono Group, ForEach, List o la suddivisione in sottocomponenti. Group non aggiunge annidamento visivo, ma ogni Group conta come un elemento.
struct ManyElementsView: View {
var body: some View {
Group {
Text("1"); Text("2"); Text("3")
Text("4"); Text("5"); Text("6")
Text("7"); Text("8"); Text("9")
}
Group {
Text("10"); Text("11"); Text("12")
}
}
}
Limitazione 2 — mancanza di supporto per alcuni costrutti. @ViewBuilder non supporta do/catch, guard, for-in (senza ForEach) e altri costrutti di controllo del flusso. Per i cicli, usa ForEach con dati identificabili. Per la gestione degli errori, usa Views separate che accettano Result o valori opzionali.
Limitazione 3 — complessità di debug. Quando si verificano errori in @ViewBuilder, il compilatore produce messaggi verbosi in cui è difficile trovare la causa principale. Problemi tipici: mancata corrispondenza dei tipi nei rami if/else, superamento del limite di 10 elementi o mancanza degli overload necessari di buildBlock.
Pattern 1: visualizzazione condizionale tramite if/else. Il caso d'uso più comune di @ViewBuilder. Permette di mostrare diverse Views in base allo stato senza usare operatori ternari o metodi factory.
struct StatusView: View {
var status: LoadStatus
@ViewBuilder
var body: some View {
switch status {
case .loading:
ProgressView("Loading...")
case .loaded(let data):
DataView(data: data)
case .error(let message):
ErrorView(message: message)
}
}
}
Pattern 2: @ViewBuilder nei parametri di funzioni e inizializzatori. Utilizzato per creare contenitori riutilizzabili che accettano Views figlie tramite una closure. Questo è il pattern standard per librerie e componenti UI.
struct SectionCard<Content: View>: View {
let title: String
@ViewBuilder let content: Content
var body: some View {
VStack(alignment: .leading) {
Text(title).font(.headline)
content
}
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(12)
}
}
Pattern 3: composizione con ForEach. @ViewBuilder funziona correttamente con ForEach, permettendo la generazione dinamica di elementi da un array di dati. Ogni elemento di ForEach conta come un'espressione nel contesto di @ViewBuilder.
ViewBuilder personalizzato è una funzione o proprietà definita dall'utente annotata con @ViewBuilder che restituisce some View. Queste funzioni permettono di incapsulare logica di visualizzazione complessa e riutilizzarla in diverse parti dell'applicazione.
struct FormRow<Content: View>: View {
let label: String
@ViewBuilder let content: Content
var body: some View {
HStack {
Text(label)
.frame(width: 120, alignment: .trailing)
content
}
}
}
// Utilizzo:
FormRow(label: "Name") {
TextField("Enter name", text: $name)
}
FormRow(label: "Gender") {
Picker("Select", selection: $gender) {
Text("Maschio").tag(Gender.male)
Text("Femmina").tag(Gender.female)
}
}
Regola importante: una funzione personalizzata con @ViewBuilder deve restituire some View, non un tipo concreto o il protocollo View. Solo un tipo opaco permette di nascondere l'implementazione concreta preservando la flessibilità di composizione.
Prestazioni: le funzioni personalizzate @ViewBuilder non aggiungono overhead rispetto al codice diretto in body. Il compilatore inlinea le chiamate e ottimizza il codice risultante. Dividere body in funzioni @ViewBuilder migliora la leggibilità senza sacrificare le prestazioni.
Domande frequenti
@ViewBuilder è un'annotazione result builder che trasforma un blocco di codice con multiple espressioni e condizioni in un unico tipo View. Permette di usare la sintassi Swift familiare (if/else, switch, espressioni opzionali) all'interno dell'UI dichiarativa di SwiftUI.
La limitazione deriva dall'implementazione di buildBlock — esiste un overload separato del metodo per ogni arità da 1 a 10. Swift non supporta i generics variadici, quindi il numero di overload è fisso. Per aggirarlo, usa Group, ForEach o sottocomponenti.
No, il protocollo View applica implicitamente @ViewBuilder alla proprietà body. Tuttavia, per proprietà personalizzate, metodi e parametri closure che restituiscono più Views, l'annotazione deve essere specificata esplicitamente. Senza di essa, il compilatore non sarà in grado di gestire espressioni multiple.
Per le espressioni opzionali, viene usato il metodo buildIf, che accetta una View opzionale e la restituisce se esiste un valore. Se il valore è nil, buildIf restituisce nil e l'elemento non viene visualizzato. Questo permette di usare if let all'interno del body.
Sì, da Swift 5.9 @ViewBuilder supporta switch tramite il metodo buildExpression. Il compilatore trasforma ogni ramo case nella corrispondente chiamata buildEither. Il supporto di switch rende il codice più leggibile rispetto ai costrutti if/else annidati.
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