NavigationStack je moderní navigační kontejner v SwiftUI, představený v iOS 16+ a nahrazující NavigationView. Podle Apple Developer Documentation, 2024 NavigationStack spravuje zásobník obrazovek prostřednictvím typově bezpečné navigační cesty (NavigationPath), podporuje hlubokou navigaci, programový návrat na kořenovou obrazovku a uchovávání stavu při změně dat. Na rozdíl od NavigationView, NavigationStack nevyžaduje zabalení do dalšího kontejneru a poskytuje přímý Binding k navigační cestě.
Hlavní
NavigationStack je kontejnerové View implementující navigaci založenou na zásobníku (LIFO). Spravuje historii přechodů, umožňuje umístit nové obrazovky na zásobník a vrátit se zpět pomocí systémového tlačítka «Zpět» nebo programově. NavigationStack je součástí SwiftUI od iOS 16, iPadOS 16, macOS 13, watchOS 9 a tvOS 16.
Hlavní inovací NavigationStack je typově bezpečná navigační cesta. Místo přímého uvádění destination při vytváření NavigationLink umístíte hodnotu (value) do cesty a registrace cílového View probíhá samostatně prostřednictvím modifikátoru .navigationDestination(for:destination:). To odděluje navigaci a zobrazení, činící kód modulárnějším a testovatelnějším.
Podle WWDC 2022 (Session 10054) NavigationStack používá nový navigační mechanismus založený na ObservableObject a životním cyklu SwiftUI. Na rozdíl od NavigationView, který se spoléhal na UINavigationController pod kapotou, je NavigationStack plně implementován v SwiftUI, což zlepšuje předvídatelnost a kompatibilitu s životním cyklem SwiftUI.
NavigationStack přijímá kořenové View a volitelnou navigační cestu (Binding k NavigationPath nebo poli Hashable hodnot). Všechny podřízené obrazovky jsou umístěny do zásobníku prostřednictvím NavigationLink nebo programového přidávání hodnot do cesty.
NavigationView byl hlavním navigačním kontejnerem SwiftUI do iOS 16. Automaticky spravoval UINavigationController pod kapotou, což vedlo k řadě problémů: nepředvídatelné chování při změně dat, potíže s programovou navigací a nedostatek typové bezpečnosti.
| Charakteristika | NavigationStack (iOS 16+) | NavigationView (iOS 13–15) |
|---|---|---|
| Typ navigace | Zásobník (LIFO) | Zásobník (LIFO) |
| Navigační cesta | Typovaná (NavigationPath) | Nepodporováno |
| Deep linking | Vestavěná podpora | Vyžaduje hacky |
| Programový návrat | Prostřednictvím cesty (pop, popToRoot) | dismiss, presentationMode |
| Pod kapotou | SwiftUI native | UINavigationController |
| Kompatibilita | iOS 16+ | iOS 13+ |
Klíčová výhoda NavigationStack — typově bezpečná navigace. Definujete cestu jako pole konkrétních typů (nebo NavigationPath pro heterogenní zásobníky) a registrujete destination pro každý typ. To eliminuje chyby neshody typů a činí navigaci předvídatelnou.
NavigationView je zastaralý v iOS 17. Apple doporučuje migraci na NavigationStack pro všechny nové projekty a při aktualizaci minimální verze na iOS 16.
NavigationPath je typ představující navigační cestu v NavigationStack. Může ukládat heterogenní hodnoty (AnyHashable) nebo být použit s konkrétním typem prostřednictvím Binding k poli [T: Hashable]. NavigationPath se automaticky kóduje a dekóduje pro uchování stavu.
struct ContentView: View {
@State private var path = NavigationPath()
var body: some View {
NavigationStack(path: $path) {
HomeView()
.navigationDestination(for: String.self) { value in
DetailView(id: value)
}
.navigationDestination(for: Int.self) { value in
NumberView(number: value)
}
}
}
func goToRoot() {
path.removeLast(path.count)
}
func pushDeepLink() {
path.append("detail_42")
}
}
Heterogenní zásobník: NavigationPath může obsahovat hodnoty různých typů, pokud implementují Hashable. Například první obrazovka může přijímat String (ID), druhá — Int (číslo), třetí — vlastní enum Route. Pro zobrazení každého typu se registruje samostatný .navigationDestination.
Podpora Codable: NavigationPath implementuje Codable, pokud jsou všechny hodnoty v cestě také Codable + Hashable. To umožňuje ukládat a obnovovat stav navigace při restartu aplikace nebo při přechodu do pozadí.
.navigationDestination(for:destination:) — modifikátor, který registruje cílové View pro konkrétní typ dat. Když NavigationLink umístí hodnotu tohoto typu do cesty, SwiftUI automaticky najde odpovídající .navigationDestination a vytvoří obrazovku.
enum AppRoute: Hashable {
case profile(UserID)
case settings
case about
}
struct AppNavigation: View {
@State private var path = NavigationPath()
var body: some View {
NavigationStack(path: $path) {
HomeView()
.navigationDestination(for: AppRoute.self) { route in
switch route {
case .profile(let id):
ProfileView(userId: id)
case .settings:
SettingsView()
case .about:
AboutView()
}
}
}
}
}
// Navigate: path.append(AppRoute.profile("user_123"))
Důležité pravidlo: .navigationDestination musí být aplikován na View, které se nachází uvnitř NavigationStack, a před tím, než NavigationLink umístí hodnotu do cesty. Obvykle se přidává ke kořenovému View nebo ke kontejneru sekce. Pokud .navigationDestination není nalezen pro typ hodnoty, přechod se neprovede.
Podle SwiftUI Engineering (2023) může být .navigationDestination zaregistrován na různých úrovních hierarchie. SwiftUI hledá nejbližší vhodný destination při pokusu o navigaci. To umožňuje přepsat destination pro jeden typ v různých částech aplikace.
Vzor 1: navigace prostřednictvím enum Route. Definujte enum s přidruženými hodnotami pro všechny obrazovky aplikace. Použijte jeden .navigationDestination pro AppRoute a switch pro směrování. To poskytuje jediný zdroj pravdy pro všechny možné přechody v aplikaci.
struct StoreView: View {
@State private var path: [ProductRoute] = []
var body: some View {
NavigationStack(path: $path) {
ProductGrid()
.navigationDestination(for: ProductRoute.self) { route in
switch route {
case .detail(let product):
ProductDetail(product: product)
case .reviews(let productId):
ReviewsView(productId: productId)
}
}
}
}
}
enum ProductRoute: Hashable {
case detail(Product)
case reviews(String)
}
Vzor 2: programová navigace a deep linking. NavigationStack umožňuje programově spravovat zásobník: přidávat, odebírat obrazovky a vrátit se na kořen. To je nezbytné pro push oznámení, hluboké odkazy a obnovení navigace po restartu.
Vzor 3: pole typů místo NavigationPath. Pokud všechny obrazovky používají jeden typ (např. String nebo vlastní enum), použijte Binding k [T]. To poskytuje přísnější typizaci a lepší výkon než NavigationPath. NavigationPath je odůvodněný pro heterogenní zásobníky s různými typy obrazovek.
Podle Point-Free (2024) je NavigationStack s enum Route preferovaným způsobem organizace navigace v SwiftUI aplikacích. Činí všechny možné přechody explicitními, typově bezpečnými a testovatelnými, což je obzvláště důležité pro velké projekty s desítkami obrazovek.
Často kladené otázky
NavigationStack — navigační kontejner SwiftUI (iOS 16+) spravující zásobník obrazovek prostřednictvím typově bezpečné cesty. Nahradil NavigationView a nabízí podporu pro deep linking, programovou navigaci a ukládání stavu.
NavigationStack používá typově bezpečnou cestu (NavigationPath) místo přímého propojení NavigationLink s destination. Podporuje programovou navigaci, deep linking a Codable pro ukládání stavu. Pracuje na SwiftUI, nikoli prostřednictvím UINavigationController.
NavigationPath je typ představující sekvenci obrazovek v zásobníku. Hodnoty přidáváte do cesty pomocí path.append() nebo prostřednictvím NavigationLink s value. Pro zobrazení každého typu se registruje .navigationDestination. NavigationPath podporuje Codable a automatické ukládání stavu.
Prostřednictvím programové správy cesty: po zpracování URL zavolejte path.append() s příslušnou hodnotou trasy. NavigationStack automaticky zobrazí cílovou obrazovku. Návrat na kořen — path.removeLast(path.count).
Ano, pokud je vaše minimální verze iOS 16+. NavigationView je zastaralý v iOS 17. Migrace poskytuje typově bezpečnou navigaci, podporu deep linking a lepší soulad s životním cyklem SwiftUI. Pro projekty s iOS 15 a nižším zatím používejte NavigationView.
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é