@ViewBuilder — је анотација result builder-а у SwiftUI, намењена декларативној изградњи хијерархије View. Према Apple Developer Documentation, 2024, @ViewBuilder претвара блок кода са више израза и условном логиком у јединствени View тип, разумљив Swift компајлеру. Без ове анотације било би немогуће користити познату декларативну синтаксу SwiftUI-ја са if/else и више елемената у телу body.
Главно
@ViewBuilder — је анотација која имплементира образац result builder (SE-0289), која омогућава SwiftUI-ју да сакупи више View-ова у једну композицију користећи декларативну синтаксу. Она аутоматски умотава вишеструке изразе, условне конструкције и опционе вредности у одговарајуће типове: TupleView, ConditionalContent, OptionalContent.
Пре појаве result builder-а, програмери су морали ручно да умотавају елементе у VStack или HStack, а за условну логику да користе тернарне операторе или фабричке методе. @ViewBuilder је учинио синтаксу SwiftUI-ја концизном и читљивом, омогућавајући писање кода који изгледа као обичан Swift са if/else и петљама.
Према Swift Evolution SE-0289, result builders су општи механизам који није везан за SwiftUI. @ViewBuilder је једна од имплементација овог механизма, поред @StringBuilder-а за изградњу стрингова и библиотечких имплементација за друге DSL-ове. У SwiftUI-ју, @ViewBuilder се користи не само за body, већ и за параметре-затвараче контејнера (VStack, HStack, ZStack, List).
У императивном UIKit-у императивно креирате UIView, подешавате његова својства и додајете га у хијерархију преко addSubview. У SwiftUI-ју са @ViewBuilder-ом декларативно описујете који View-ови треба да буду приказани, а SwiftUI сам управља креирањем, ажурирањем и брисањем елемената на основу промена стања.
Result builder — је Swift механизам који претвара низ израза у једну сложену вредност кроз статичке методе buildBlock, buildOptional, buildEither и друге. Када компајлер види анотацију @ViewBuilder, аутоматски примењује ове методе на блок кода током компајлирања.
@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 прима од 1 до 10 израза и враћа TupleView. Свака арност (број израза) има свој overload buildBlock-а: од buildBlock<C0> до buildBlock<C0, C1, ..., C9>. Управо зато је број елемената у једном @ViewBuilder блоку ограничен на 10.
buildEither (first/second) обрађује if/else конструкције. Свака грана се прослеђује одговарајућем методу, а резултат се умотава у ConditionalContent — тип који скрива специфичне типове грана и пружа јединствени интерфејс за SwiftUI.
У SwiftUI-ју, својство body је већ имплицитно анотирано @ViewBuilder-ом — не видите ову анотацију у коду, али компајлер је аутоматски примењује. Међутим, за корисничка својства која враћају више View-ова или за параметре-затвараче, анотацију треба експлицитно навести.
Ограничење 1 — 10 елемената у блоку. Ово је најпознатије ограничење @ViewBuilder-а. Ако треба приказати више од 10 елемената на истом нивоу, компајлер ће пријавити грешку. Можете га заобићи помоћу Group, ForEach, List или поделом на подкомпоненте. Group не додаје визуелно угњежђење, али се сваки Group рачуна као један елемент.
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")
}
}
}
Ограничење 2 — недостатак подршке за неке конструкције. @ViewBuilder не подржава do/catch, guard, for-in (без ForEach) и друге контролне конструкције. За петље користите ForEach са идентификујућим подацима. За обраду грешака користите одвојене View-ове који примају Result или опционе вредности.
Ограничење 3 — тешкоће отклањања грешака. Код грешака у @ViewBuilder-у, компајлер генерише опширне поруке у којима је тешко пронаћи основни узрок. Типични проблеми: неусклађеност типова у if/else гранама, прекорачење лимита од 10 елемената или недостатак потребног overload-а buildBlock-а.
Образац 1: условно приказивање кроз if/else. Најчешћи сценариј коришћења @ViewBuilder-а. Омогућава приказивање различитих View-ова у зависности од стања без коришћења тернарних оператора или фабричких метода.
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)
}
}
}
Образац 2: @ViewBuilder у параметрима функција и иницијализатора. Користи се за креирање контејнера за вишекратну употребу који примају подређене View-ове кроз затварач. Ово је стандардни образац за библиотеке и 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)
}
}
Образац 3: композиција са ForEach. @ViewBuilder исправно ради са ForEach, омогућавајући динамичко генерисање елемената из низа података. Сваки ForEach елемент се рачуна као један израз у контексту @ViewBuilder-а.
Прилагођени ViewBuilder — је корисничка функција или својство анотирано @ViewBuilder-ом које враћа some View. Такве функције омогућавају капсулирање сложене логике приказа и њено поновно коришћење у различитим деловима апликације.
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
}
}
}
// Употреба:
FormRow(label: "Name") {
TextField("Enter name", text: $name)
}
FormRow(label: "Gender") {
Picker("Select", selection: $gender) {
Text("Мушки").tag(Gender.male)
Text("Женски").tag(Gender.female)
}
}
Важно правило: прилагођена функција са @ViewBuilder-ом мора да враћа some View, а не конкретан тип или протокол View. Само opaque тип омогућава скривање конкретне имплементације и очување флексибилности композиције.
Перформансе: прилагођене @ViewBuilder функције не додају додатни трошак у поређењу са директним кодом у body-ју. Компајлер уграђује позиве и оптимизује резултујући код. Подела body-ја на @ViewBuilder функције побољшава читљивост без губитка перформанси.
Често постављана питања
@ViewBuilder — је анотација result builder-а која претвара блок кода са више израза и услова у један View тип. Омогућава коришћење познате Swift синтаксе (if/else, switch, опциони изрази) унутар декларативног SwiftUI интерфејса.
Ограничење је повезано са имплементацијом buildBlock-а — за сваку арност од 1 до 10 постоји посебан overload методе. Swift не подржава variadic generics, па је број overload-ова фиксан. Да бисте то заобишли, користите Group, ForEach или подкомпоненте.
Не, View протокол имплицитно примењује @ViewBuilder на својство body. Међутим, за корисничка својства, методе и параметре-затвараче који враћају више View-ова, анотацију треба експлицитно навести. Без ње, компајлер неће моћи да обради вишеструке изразе.
За опционе изразе користи се метод buildIf, који прима опциони View и враћа га ако вредност постоји. Ако је вредност nil — buildIf враћа nil и елемент се не приказује. Ово омогућава коришћење if let у телу body-ја.
Да, од Swift 5.9 @ViewBuilder подржава switch преко buildExpression метода. Компајлер претвара сваку case грану у одговарајући buildEither позив. Подршка за switch чини код читљивијим у поређењу са угњежденим if/else конструкцијама.
Резиме
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође