Vlastnost body — ústřední prvek protokolu View ve SwiftUI, který určuje, jaký obsah se zobrazuje na obrazovce. Podle Apple Developer Documentation, 2024 je body jediným povinným požadavkem protokolu View a vrací typ, který je v souladu s tímto protokolem. SwiftUI volá body při každé změně stavu, aby vytvořil a porovnal nový strom prvků.
Hlavní body
body — je vypočítávaná vlastnost (computed property), která je jediným povinným požadavkem protokolu View. Každá struktura, která je v souladu s View, musí implementovat body. Vlastnost vrací obsah, který SwiftUI zobrazuje na obrazovce — může to být text, obrázek, tlačítko, kontejner s vnořenými prvky nebo jakýkoli jiný typ, který je v souladu s protokolem View.
Signatura body je vždy pevná: var body: some View { get }. Návratový typ — some View (neprůhledný typ), nikoli konkrétní typ. To znamená, že různá View mohou vracet různé konkrétní typy v body, ale Swift kompilátor fixuje konkrétní typ pro každou implementaci ve fázi kompilace.
Podle WWDC 2022 je body vstupním bodem do deklarativního popisu rozhraní. Na rozdíl od UIKit, kde imperativně vytváříte a konfigurujete UIView, ve SwiftUI deklarativně popisujete, co má být zobrazeno, a SwiftUI sám vypočítá, jak to implementovat.
Body by se měl chovat jako čistá funkce — se stejnými vstupními daty (vlastnosti struktury a stav) by měl vracet stejný strom View. Pokud body závisí na vnějším proměnlivém stavu (globální proměnné, UserDefaults bez obalu @AppStorage), chování se stává nepředvídatelným a SwiftUI může překreslovat obrazovku nesprávně.
Vypočítávaná vlastnost body neukládá hodnotu — vypočítává se pokaždé, když je k ní přistupováno. Když SwiftUI zjistí, že se stav změnil, znovu vytvoří strukturu View a přečte novou hodnotu body, aby získal aktuální strom prvků pro zobrazení.
struct CounterView: View {
@State private var count = 0
var body: some View {
VStack {
Text("Počítadlo: \(count)")
.font(.largeTitle)
Button("Zvýšit") {
count += 1
}
.padding()
.background(.blue)
.foregroundColor(.white)
.cornerRadius(8)
}
}
}
V tomto příkladu body vrací VStack obsahující Text a tlačítko s modifikátory. Po stisknutí tlačítka se vlastnost @State count zvýší, SwiftUI znovu vytvoří strukturu CounterView a znovu zavolá body, aby získal aktualizovaný strom s novou hodnotou Text.
Modifikátory (.font, .padding, .background, .foregroundColor, .cornerRadius) nemění původní View, ale obalují jej do ModifiedContent — nového typu, který přidává modifikaci. Každý modifikátor vytváří další úroveň vnoření, což je důležité zvážit pro výkon.
some View v návratovém typu body — není jen konvence, ale povinný požadavek kompilátoru. Swift vyžaduje, aby všechny návratové cesty v body měly stejný konkrétní typ. Bez @ViewBuilder nemůžete vrátit Text v jedné větvi a Button v druhé — kompilátor způsobí chybu.
struct ConditionalView: View {
var isReady: Bool
@ViewBuilder
var body: some View {
if isReady {
Text("Připraveno")
.foregroundColor(.green)
} else {
ProgressView()
}
}
}
@ViewBuilder na body umožňuje použití podmíněné logiky (if/else, switch) bez chyb kompilace. ViewBuilder automaticky obaluje různé větve do ConditionalContent — speciálního typu, který skrývá rozdíly konkrétních typů. To je klíčová schopnost pro vytváření dynamických rozhraní.
Bez @ViewBuilder se kompilátor pokouší odvodit jednotný typ pro všechny návratové cesty. Pokud jsou typy odlišné — nastane chyba. Proto SwiftUI implicitně aplikuje @ViewBuilder na body v deklaracích View, i když v uživatelském kódu musí být anotace umístěna explicitně pro vlastní metody a vlastnosti vracející více View.
Použití some View místo konkrétního typu nesnižuje výkon — kompilátor ve fázi kompilace zná přesný typ a generuje přímý kód bez dynamického odesílání. AnyView naproti tomu používá vymazání typu (type erasure) s režií balení do existenciálního kontejneru.
body je volán SwiftUI ve třech hlavních scénářích: při prvním zobrazení View, při změně @State/@Binding/@ObservedObject/@StateObject a při změně rodičovského View, které předává nové hodnoty přes inicializátor. SwiftUI může také volat body při změně hodnot prostředí (@Environment).
Frekvence volání body by vás neměla znepokojovat — SwiftUI optimalizuje překreslování prostřednictvím mechanismu identity. Každé View v hierarchii má jedinečný identifikátor. Pokud se identita a vstupní data nezměnily — body není volán, i když se rodičovské View překreslilo. Toho je dosaženo pomocí Equatable porovnání a stability struktur.
struct ParentView: View {
var body: some View {
ChildView(name: "Alice") // Stabilní identita
}
}
struct ChildView: View {
let name: String
var body: some View {
Text("Ahoj, \(name)!")
}
}
V tomto příkladu, pokud se ParentView překreslí, ale předá stejnou hodnotu name — ChildView.body není volán. SwiftUI porovnává vstupní data struktury a pokud se nezměnila, přeskočí překreslení podřízené komponenty. Toto je mechanismus diferenciace pohledu (view differentiation).
Existuje několik pastí vedoucích k neočekávanému volání body: používání tříd bez ObservableObject, předávání uzávěrů vytvořených uvnitř body (každé vytvoření uzávěru dává novou identitu) a nesprávné použití EquatableView. Pokud je body volán příliš často — zkontrolujte stabilitu identity všech podřízených komponent.
První pravidlo: body by měl být minimální. Složitou logiku přesuňte do samostatných vypočítávaných vlastností nebo metod vracejících View. To zlepšuje čitelnost a umožňuje SwiftUI přesněji určit, které části hierarchie se změnily. Rozdělujte velká body na podkomponenty s jasnými hranicemi odpovědnosti.
Druhé pravidlo: nepoužívejte body k provádění práce. Načítání dat, práce se sítí, zápis do databáze — vše by mělo probíhat mimo body, v úkolech (task), modifikátorech onChange nebo prostřednictvím ObservableObject. Body je určen výhradně pro deklaraci rozhraní.
Třetí pravidlo: používejte vlastnost EquatableView nebo vlastní protokol Equatable pro View, pokud standardní porovnání struktur nestačí. To umožňuje explicitně SwiftUI naznačit, kdy podřízené View vyžaduje překreslení a vyhnout se zbytečným voláním body.
Čtvrté pravidlo: pokud body obsahuje složité výpočty (formátování, filtrování, řazení) — používejte @State pro ukládání do mezipaměti výsledku nebo přesuňte výpočty do samostatné metody volané z onChange. Opakované výpočty v body při každé aktualizaci stavu — častá příčina zpomalování animací.
Páté pravidlo: pro seznamy (List, ForEach) zajistěte stabilní identifikátory prostřednictvím parametru id. Bez stabilní identity ForEach znovu vytváří všechny prvky při každé změně, volá body pro každý z nich, i když se změnil pouze jeden prvek.
Často kladené otázky
body — vypočítávaná vlastnost protokolu View, která vrací obsah k zobrazení. Je to jediný povinný požadavek protokolu. Návratový typ — some View, což umožňuje SwiftUI optimalizovat hierarchii ve fázi kompilace.
Ano, SwiftUI volá body při každé změně stavu (@State, @Binding, @ObservedObject) nebo vstupních dat. Toto je normální chování deklarativního frameworku. SwiftUI optimalizuje frekvenci volání prostřednictvím mechanismu identity a Equatable porovnání.
some View — neprůhledný typ, který umožňuje skrýt konkrétní implementaci. Kompilátor fixuje typ ve fázi kompilace a zajišťuje výkon přímého volání. To poskytuje flexibilitu: můžete změnit návratový typ beze změny signatury.
Ne, body nemůže být volitelný — návratový typ some View nepovoluje nil. Pokud potřebujete prvek podmíněně skrýt, použijte podmíněnou logiku uvnitř @ViewBuilder nebo vraťte EmptyView, který nezabírá místo v hierarchii.
Každý modifikátor vytváří novou vrstvu ModifiedContent, zvyšující hloubku hierarchie. Pro většinu obrazovek (do 50 modifikátorů) je vliv nepostřehnutelný. Nadměrné množství modifikátorů (stovky) může zpomalit diffing. Seskupujte související modifikátory do vlastních rozšíření.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také