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 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) |
| Путь навигации | Typed (NavigationPath) | Не поддерживается |
| Deep linking | Встроенная поддержка | Требует хаков |
| Программный возврат | Через путь (pop, popToRoot) | dismiss, presentationMode |
| Под капотом | SwiftUI native | 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-уведомлений, Deeplink-ов и восстановления навигации после перезапуска.
Паттерн 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 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также