NavigationStack — це сучасний навігаційний контейнер у SwiftUI, представлений у iOS 16+ і який замінив NavigationView. Згідно з документацією Apple Developer, 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 (Сесія 10054), NavigationStack використовує новий механізм навігації, заснований на ObservableObject та SwiftUI lifecycle. На відміну від NavigationView, який покладався на UINavigationController всередині, NavigationStack повністю реалізований на SwiftUI, що покращує передбачуваність і сумісність із SwiftUI lifecycle.
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 нативний | UINavigationController |
| Сумісність | iOS 16+ | iOS 13+ |
Ключова перевага NavigationStack — типобезпечна навігація. Ви визначаєте шлях як масив конкретних типів (або NavigationPath для гетерогенних стеків) і реєструєте destination для кожного типу. Це виключає помилки невідповідності типів і робить навігацію передбачуваною.
NavigationView deprecated в 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-сповіщень, deep link-ів та відновлення навігації після перезапуску.
Паттерн 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 deprecated в iOS 17. Міграція дає типобезпечну навігацію, підтримку deep linking та кращу відповідність SwiftUI lifecycle. Для проектів з iOS 15 і нижче поки використовуйте NavigationView.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.