Својство body — централни елемент протокола View у SwiftUI, који одређује који садржај се приказује на екрану. Према Apple Developer Documentation, 2024, body је једини обавезни захтев протокола View и враћа тип који је у складу са истим протоколом. SwiftUI позива body при свакој промени стања ради изградње и поређења новог стабла елемената.
Главно
body — је израчунава својство (computed property) које је једини обавезни захтев протокола View. Свака структура која је у складу са View мора да имплементира body. Својство враћа садржај који SwiftUI приказује на екрану — то може бити текст, слика, дугме, контејнер са угњежденим елементима или било који други тип који је у складу са протоколом View.
Потпис body је увек фиксан: var body: some View { get }. Тип повратка — some View (непрозиран тип), а не конкретан тип. То значи да различити View-ови могу враћати различите конкретне типове у body, али Swift компајлер фиксира конкретан тип за сваку имплементацију у фази компилације.
Према WWDC 2022, body је улазна тачка у декларативни опис интерфејса. За разлику од UIKit-а, где императивно креирате и конфигуришете UIView, у SwiftUI декларативно описујете шта треба да се прикаже, а SwiftUI сам израчунава како да то имплементира.
body треба да се понаша као чиста функција — са истим улазним подацима (својства структуре и стање) треба да враћа исто стабло View. Ако body зависи од спољашњег променљивог стања (глобалне променљиве, UserDefaults без омотнице @AppStorage), понашање постаје непредвидиво, а SwiftUI може некоректно да прецрта екран.
Израчунава својство body не чува вредност — израчунава се сваки пут када му се приступи. Када SwiftUI утврди да се стање променило, поново креира структуру View и чита нову вредност body да би добио ажурирано стабло елемената за приказивање.
struct CounterView: View {
@State private var count = 0
var body: some View {
VStack {
Text("Бројач: \(count)")
.font(.largeTitle)
Button("Повећај") {
count += 1
}
.padding()
.background(.blue)
.foregroundColor(.white)
.cornerRadius(8)
}
}
}
У овом примеру, body враћа VStack који садржи Text и дугме са модификаторима. При притиску на дугме, @State својство count се повећава, SwiftUI поново креира структуру CounterView и поново позива body да би добио ажурирано стабло са новом вредношћу Text.
Модификатори (.font, .padding, .background, .foregroundColor, .cornerRadius) не мењају оригинални View, већ га обавијају у ModifiedContent — нови тип који додаје модификацију. Сваки модификатор ствара још један ниво угњежђења, што је важно узети у обзир за перформансе.
some View у повратном типу body — није само конвенција, већ обавезан захтев компајлера. Swift захтева да all путеви повратка у body имају исти конкретан тип. Без @ViewBuilder не можете вратити Text у једној грани и Button у другој — компајлер ће пријавити грешку.
struct ConditionalView: View {
var isReady: Bool
@ViewBuilder
var body: some View {
if isReady {
Text("Спремно")
.foregroundColor(.green)
} else {
ProgressView()
}
}
}
@ViewBuilder на body омогућава употребу условне логике (if/else, switch) без грешака компајлера. ViewBuilder аутоматски обавија различите гране у ConditionalContent — посебан тип који скрива разлике конкретних типова. Ово је кључна могућност за изградњу динамичких интерфејса.
Без @ViewBuilder компајлер покушава да закључи јединствени тип за све путеве повратка. Ако су типови различити — настаје грешка. Зато SwiftUI нејавно примењује @ViewBuilder на body у декларацијама View, иако у корисничком коду анотацију треба поставити експлицитно за прилагођене методе и својства која враћају више View-ова.
Коришћење some View уместо конкретног типа не смањује перформансе — компајлер у фази компилације зна тачан тип и генерише директан код без динамичке диспечерије. AnyView, насупрот томе, користи брисање типа (type erasure) са додатним оптерећењем паковања у егзистенцијални контејнер.
body позива SwiftUI у три главна сценарија: при првом приказивању View, при промени @State/@Binding/@ObservedObject/@StateObject и при промени родитељског View које преноси нове вредности кроз иницијализатор. SwiftUI такође може позвати body при промени вредности окружења (@Environment).
Учесталост позивања body не би требало да вас брине — SwiftUI оптимизује прецртавање кроз механизам идентитета. Сваки View у хијерархији има јединствени идентификатор. Ако се идентитет и улазни подаци нису променили — body се не позива, чак и ако се родитељски View прецртао. То се постиже кроз Equatable поређење и стабилност структура.
struct ParentView: View {
var body: some View {
ChildView(name: "Alice") // Стабилан идентитет
}
}
struct ChildView: View {
let name: String
var body: some View {
Text("Здраво, \(name)!")
}
}
У овом примеру, ако се ParentView прецрта, али преноси исту вредност name — ChildView.body се не позива. SwiftUI пореди улазне податке структуре и, ако се нису променили, прескаче прецртавање компоненте детета. Ово је механизам диференцијације приказа (view differentiation).
Постоји неколико замки које доводе до неочекиваног позивања body: коришћење класа без ObservableObject, преношење затворења створених унутар body (свако креирање затворења даје нови идентитет) и неправилно коришћење EquatableView. Ако се body позива пречесто — проверите стабилност идентитета свих компоненти деце.
Прво правило: body треба да буде минималан. Издвајајте сложену логику у одвојена израчунава својства или методе које враћају View. То побољшава читљивости и омогућава SwiftUI да прецизније одреди који делови хијерархије су се променили. Раздвајајте велика body-ја на подкомпоненте са јасним границама одговорности.
Друго правило: не користите body за обављање посла. Учитавање података, рад са мрежом, уписивање у базу података — све то треба да се дешава ван body, у задацима (task), модификаторима onChange или кроз ObservableObject. body је намењен искључиво за декларацију интерфејса.
Треће правило: користите својство EquatableView или прилагођени протокол Equatable за View, ако стандардно поређење структура није довољно. Ово омогућава да експлицитно укажете SwiftUI када компонента детета захтева прецртавање и избегнете непотребне позиве body.
Четврто правило: ако body садржи сложена израчунавања (форматирање, филтрирање, сортирање) — користите @State за кеширање резултата или издвојите израчунавања у посебан метод позван из onChange. Поновљена израчунавања у body при сваком ажурирању стања — чест узрок успоравања анимација.
Пето правило: за листе (List, ForEach) обезбедите стабилне идентификаторе кроз параметар id. Без стабилног идентитета ForEach поново креира све елементе при било којој промени, позивајући body за сваки од њих, чак и ако се променио само један елемент.
Често постављана питања
body — израчунава својство протокола View које враћа садржај за приказивање. То је једини обавезни захтев протокола. Тип повратка — some View, што омогућава SwiftUI да оптимизује хијерархију у фази компилације.
Да, SwiftUI позива body при свакој промени стања (@State, @Binding, @ObservedObject) или улазних података. Ово је нормално понашање декларативног оквира. SwiftUI оптимизује учесталост позива кроз механизам идентитета и Equatable поређење.
some View — непрозиран тип који омогућава скривање конкретне имплементације. Компајлер фиксира тип у фази компилације, обезбеђујући перформансе директног позива. Ово даје флексибилност: можете променити повратни тип без промене потписа.
Не, body не може бити опционални — повратни тип some View не дозвољава nil. Ако треба да сакријете елемент условно, користите условну логику унутар @ViewBuilder или вратите EmptyView, који не заузима место у хијерархији.
Сваки модификатор ствара нови слој ModifiedContent, повећавајући дубину хијерархије. За већину екрана (до 50 модификатора) утицај је неприметан. Прекомеран број модификатора (стотине) може успорити diffing. Групишите повезане модификаторе у прилагођена проширења.
Резиме
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође