@ViewBuilder — is een result builder annotatie in SwiftUI, bedoeld voor het declaratief opbouwen van de View-hiërarchie. Volgens Apple Developer Documentation, 2024, zet @ViewBuilder een codeblok met meerdere expressies en conditionele logica om in een enkel View-type dat begrijpelijk is voor de Swift-compiler. Zonder deze annotatie zou het onmogelijk zijn om de vertrouwde declaratieve syntaxis van SwiftUI met if/else en meerdere elementen in de body te gebruiken.
Belangrijkste punten
@ViewBuilder — is een annotatie die het result builder patroon (SE-0289) implementeert, waarmee SwiftUI meerdere Views in één compositie kan verzamelen met behulp van declaratieve syntaxis. Het wikkelt automatisch meerdere expressies, conditionele constructies en optionele waarden in de juiste types: TupleView, ConditionalContent, OptionalContent.
Voordat result builder bestond, moesten ontwikkelaars elementen handmatig in VStack of HStack wikkelen en voor conditionele logica ternaire operatoren of factory-methoden gebruiken. @ViewBuilder maakte de SwiftUI-syntaxis beknopt en leesbaar, waardoor code kon worden geschreven die eruitziet als gewoon Swift met if/else en loops.
Volgens Swift Evolution SE-0289 zijn result builders een algemeen mechanisme dat niet aan SwiftUI is gebonden. @ViewBuilder is een van de implementaties van dit mechanisme, naast @StringBuilder voor het bouwen van strings en bibliotheekimplementaties voor andere DSL's. In SwiftUI wordt @ViewBuilder niet alleen voor body gebruikt, maar ook voor de closure-parameters van containers (VStack, HStack, ZStack, List).
In imperatieve UIKit maak je imperatief een UIView aan, configureer je de eigenschappen en voeg je deze toe aan de hiërarchie via addSubview. In SwiftUI met @ViewBuilder beschrijf je declaratief welke Views moeten worden weergegeven, en SwiftUI beheert zelf het aanmaken, bijwerken en verwijderen van elementen op basis van statuswijzigingen.
Result builder — is een Swift-mechanisme dat een reeks expressies omzet in één samengestelde waarde via statische methoden buildBlock, buildOptional, buildEither en andere. Wanneer de compiler de @ViewBuilder-annotatie ziet, past hij deze methoden automatisch toe op het codeblok tijdens het compileren.
@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 accepteert 1 tot 10 expressies en retourneert TupleView. Elke ariteit (aantal expressies) heeft zijn eigen overload van buildBlock: van buildBlock<C0> tot buildBlock<C0, C1, ..., C9>. Daarom is het aantal elementen in één @ViewBuilder-blok beperkt tot 10.
buildEither (first/second) verwerkt if/else-constructies. Elke tak wordt naar de bijbehorende methode gestuurd en het resultaat wordt ingepakt in ConditionalContent — een type dat de specifieke types van de takken verbergt en een uniforme interface biedt voor SwiftUI.
In SwiftUI is de body-eigenschap al impliciet geannoteerd met @ViewBuilder — je ziet deze annotatie niet in de code, maar de compiler past deze automatisch toe. Voor gebruikerseigenschappen die meerdere Views retourneren of voor closure-parameters moet de annotatie echter expliciet worden opgegeven.
Beperking 1 — 10 elementen in een blok. Dit is de bekendste beperking van @ViewBuilder. Als je meer dan 10 elementen op één niveau moet weergeven, geeft de compiler een foutmelding. Je kunt dit omzeilen met Group, ForEach, List of door het op te splitsen in subcomponenten. Group voegt geen visuele nesting toe, maar elke Group telt als één element.
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")
}
}
}
Beperking 2 — gebrek aan ondersteuning voor sommige constructies. @ViewBuilder ondersteunt geen do/catch, guard, for-in (zonder ForEach) en andere controlestructuren. Gebruik voor loops ForEach met identificeerbare gegevens. Gebruik voor foutafhandeling aparte Views die Result of optionele waarden accepteren.
Beperking 3 — moeilijkheid van debuggen. Bij fouten in @ViewBuilder genereert de compiler uitgebreide berichten waarin het moeilijk is om de oorzaak te vinden. Typische problemen: type-mismatch in if/else-takken, overschrijding van de limiet van 10 elementen of het ontbreken van de vereiste buildBlock-overload.
Patroon 1: conditionele weergave via if/else. Het meest voorkomende gebruiksscenario van @ViewBuilder. Maakt het mogelijk om verschillende Views weer te geven afhankelijk van de status, zonder ternaire operatoren of factory-methoden.
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)
}
}
}
Patroon 2: @ViewBuilder in functie- en init-parameters. Gebruikt voor het maken van herbruikbare containers die onderliggende Views accepteren via een closure. Dit is een standaard patroon voor bibliotheken en UI-componenten.
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)
}
}
Patroon 3: compositie met ForEach. @ViewBuilder werkt correct met ForEach, waardoor dynamische generatie van elementen uit een gegevensarray mogelijk is. Elk ForEach-element telt als één expressie in de context van @ViewBuilder.
Aangepaste ViewBuilder — is een gebruikersfunctie of -eigenschap geannoteerd met @ViewBuilder die some View retourneert. Dergelijke functies maken het mogelijk complexe weergavelogica in te kapselen en opnieuw te gebruiken in verschillende delen van de applicatie.
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
}
}
}
// Gebruik:
FormRow(label: "Name") {
TextField("Enter name", text: $name)
}
FormRow(label: "Gender") {
Picker("Select", selection: $gender) {
Text("Mannelijk").tag(Gender.male)
Text("Vrouwelijk").tag(Gender.female)
}
}
Belangrijke regel: een aangepaste functie met @ViewBuilder moet some View retourneren, niet een specifiek type of het View-protocol. Alleen het opaque type maakt het mogelijk de specifieke implementatie te verbergen en de flexibiliteit van de compositie te behouden.
Prestaties: aangepaste @ViewBuilder-functies voegen geen overhead toe in vergelijking met directe code in body. De compiler inlineert aanroepen en optimaliseert de resulterende code. Het opsplitsen van body in @ViewBuilder-functies verbetert de leesbaarheid zonder prestatieverlies.
Veelgestelde vragen
@ViewBuilder — is een result builder annotatie die een codeblok met meerdere expressies en voorwaarden omzet in een enkel View-type. Het maakt het mogelijk om vertrouwde Swift-syntaxis (if/else, switch, optionele expressies) te gebruiken binnen de declaratieve UI van SwiftUI.
De beperking houdt verband met de implementatie van buildBlock — voor elke ariteit van 1 tot 10 bestaat er een aparte overload van de methode. Swift ondersteunt geen variadic generics, dus het aantal overloads is vast. Om dit te omzeilen, gebruik je Group, ForEach of subcomponenten.
Nee, het View-protocol past impliciet @ViewBuilder toe op de body-eigenschap. Voor gebruikerseigenschappen, methoden en closure-parameters die meerdere Views retourneren, moet de annotatie echter expliciet worden aangegeven. Zonder dit kan de compiler geen meerdere expressies verwerken.
Voor optionele expressies wordt de methode buildIf gebruikt, die een optionele View accepteert en deze retourneert als de waarde bestaat. Als de waarde nil is — retourneert buildIf nil en wordt het element niet weergegeven. Dit maakt het gebruik van if let in de body mogelijk.
Ja, vanaf Swift 5.9 ondersteunt @ViewBuilder switch via de buildExpression-methode. De compiler zet elke case-tak om in een overeenkomstige buildEither-aanroep. Ondersteuning voor switch maakt de code leesbaarder in vergelijking met geneste if/else-constructies.
Samenvatting
We ontwikkelen een mobiele applicatie turnkey
IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.
Lees ook