NavigationStack е модерен навигационен контейнер в SwiftUI, представен в iOS 16+ и заменил NavigationView. Според Apple Developer Documentation, 2024, NavigationStack управлява стека от екрани чрез типобезопасен навигационен път (NavigationPath), поддържа дълбока навигация, програмно връщане към началния екран и запазване на състоянието при промяна на данни. За разлика от NavigationView, NavigationStack не изисква обвиване в допълнителен контейнер и предоставя директен Binding към навигационния път.
Основни
NavigationStack е контейнерно View, което реализира навигация на основата на стек (LIFO). То управлява историята на преходите, позволявайки поставяне на нови екрани в стека и връщане назад чрез системния бутон «Назад» или програмно. NavigationStack е част от SwiftUI от iOS 16, iPadOS 16, macOS 13, watchOS 9 и tvOS 16.
Основното нововъведение на NavigationStack е типобезопасният навигационен път. Вместо директно посочване на destination при създаване на NavigationLink, поставяте стойност (value) в пътя, а регистрацията на целевото View става отделно чрез модификатора .navigationDestination(for:destination:). Това разделя навигацията и показването, правейки кода по-модулен и тестваем.
Според WWDC 2022 (Session 10054), NavigationStack използва нов механизъм за навигация, основан на ObservableObject и жизнения цикъл на SwiftUI. За разлика от NavigationView, който разчиташе на UINavigationController под капака, NavigationStack е изцяло имплементиран в SwiftUI, което подобрява предвидимостта и съвместимостта с жизнения цикъл на SwiftUI.
NavigationStack приема кореново View и опционален навигационен път (Binding към NavigationPath или масив от Hashable стойности). Всички подчинени екрани се поставят в стека чрез NavigationLink или програмно добавяне на стойности в пътя.
NavigationView беше основният навигационен контейнер на SwiftUI до iOS 16. Той автоматично управляваше UINavigationController под капака, което водеше до редица проблеми: непредвидимо поведение при промяна на данни, трудности с програмната навигация и липса на типова безопасност.
| Характеристика | NavigationStack (iOS 16+) | NavigationView (iOS 13–15) |
|---|---|---|
| Тип навигация | Стек (LIFO) | Стек (LIFO) |
| Навигационен път | Типизиран (NavigationPath) | Не се поддържа |
| Deep linking | Вградена поддръжка | Изисква хакве |
| Програмно връщане | Чрез път (pop, popToRoot) | dismiss, presentationMode |
| Под капака | SwiftUI native | UINavigationController |
| Съвместимост | iOS 16+ | iOS 13+ |
Ключово предимство на NavigationStack — типобезопасна навигация. Определяте пътя като масив от конкретни типове (или NavigationPath за хетерогенни стекове) и регистрирате destination за всеки тип. Това елиминира грешките от несъвместимост на типове и прави навигацията предвидима.
NavigationView е остарял в iOS 17. Apple препоръчва миграция към NavigationStack за всички нови проекти и при актуализиране на минималната версия до iOS 16.
NavigationPath е тип, представляващ навигационния път в NavigationStack. Може да съхранява хетерогенни стойности (AnyHashable) или да се използва с конкретен тип чрез Binding към масив [T: Hashable]. NavigationPath автоматично се кодира и декодира за запазване на състоянието.
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")
}
}
Хетерогенен стек: NavigationPath може да съдържа стойности от различни типове, ако имплементират Hashable. Например първият екран може да приема String (ID), вторият — Int (номер), третият — персонализиран enum Route. За показване на всеки тип се регистрира отделен .navigationDestination.
Поддръжка на Codable: NavigationPath имплементира Codable, ако всички стойности в пътя също са Codable + Hashable. Това позволява запазване и възстановяване на състоянието на навигацията при рестартиране на приложението или преминаване във фонов режим.
.navigationDestination(for:destination:) — модификатор, регистриращ целево View за конкретен тип данни. Когато NavigationLink постави стойност от този тип в пътя, SwiftUI автоматично намира съответния .navigationDestination и създава екрана.
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"))
Важно правило: .navigationDestination трябва да бъде приложен към View, което се намира вътре в NavigationStack, и преди NavigationLink да постави стойност в пътя. Обикновено се добавя към кореновото View или към контейнера на секцията. Ако .navigationDestination не бъде намерен за типа на стойността, преходът няма да се осъществи.
Според SwiftUI Engineering (2023), .navigationDestination може да бъде регистриран на различни нива на йерархията. SwiftUI търси най-близкия подходящ destination при опит за навигация. Това позволява предефиниране на destination за един тип в различни части на приложението.
Модел 1: навигация чрез enum Route. Дефинирайте enum с асоциирани стойности за всички екрани на приложението. Използвайте един .navigationDestination за AppRoute и switch за маршрутизация. Това осигурява единен източник на истина за всички възможни преходи в приложението.
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)
}
Модел 2: програмна навигация и deep linking. NavigationStack позволява програмно управление на стека: добавяне, премахване на екрани и връщане към корена. Това е необходимо за push известия, дълбоки връзки и възстановяване на навигацията след рестарт.
Модел 3: масив от типове вместо NavigationPath. Ако всички екрани използват един тип (например String или персонализиран enum), използвайте Binding към [T]. Това осигурява по-строга типизация и по-добра производителност от NavigationPath. NavigationPath е оправдан за хетерогенни стекове с различни типове екрани.
Според Point-Free (2024), NavigationStack с enum Route е предпочитаният начин за организиране на навигация в SwiftUI приложения. Той прави всички възможни преходи явни, типобезопасни и тестваеми, което е особено важно за големи проекти с десетки екрани.
Често задавани въпроси
NavigationStack — навигационен контейнер SwiftUI (iOS 16+), управляващ стека от екрани чрез типобезопасен път. Замени NavigationView, предлагайки поддръжка за deep linking, програмна навигация и запазване на състояние.
NavigationStack използва типобезопасен път (NavigationPath) вместо директно свързване на NavigationLink с destination. Поддържа програмна навигация, deep linking и Codable за запазване на състояние. Работи на SwiftUI, а не чрез UINavigationController.
NavigationPath е тип, представляващ последователността от екрани в стека. Добавяте стойности в пътя чрез path.append() или чрез NavigationLink с value. За показване на всеки тип се регистрира .navigationDestination. NavigationPath поддържа Codable и автоматично запазване на състояние.
Чрез програмно управление на пътя: след обработка на URL, извикайте path.append() със съответната стойност на маршрута. NavigationStack автоматично ще покаже целевия екран. Връщане към корена — path.removeLast(path.count).
Да, ако минималната ви версия е iOS 16+. NavigationView е остарял в iOS 17. Миграцията дава типобезопасна навигация, поддръжка на deep linking и по-добро съответствие с жизнения цикъл на SwiftUI. За проекти с iOS 15 и по-ниски все още използвайте NavigationView.
Обобщение
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също