NavigationStack: что это, навигационный стек в SwiftUI

Автор: IT Sectr Опубликовано: 2026-06-24 Время чтения: 6 мин

NavigationStack — это современный навигационный контейнер в SwiftUI, представленный в iOS 16+ и заменивший NavigationView. По данным Apple Developer Documentation, 2024, NavigationStack управляет стеком экранов через типобезопасный путь навигации (NavigationPath), поддерживает глубокую навигацию, программный возврат на корневой экран и сохранение состояния при изменении данных. В отличие от NavigationView, NavigationStack не требует оборачивания в дополнительный контейнер и предоставляет прямой Binding к пути навигации.

Главное

  • NavigationStack — новый навигационный контейнер SwiftUI (iOS 16+)
  • NavigationPath — типобезопасный путь для управления стеком экранов
  • Deep linking — поддержка программной навигации через путь
  • NavigationDestination — регистрация целевых экранов по типам данных
  • Состояние навигации сохраняется и восстанавливается автоматически

Что такое NavigationStack в SwiftUI?

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

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 nativeUINavigationController
СовместимостьiOS 16+iOS 13+

Ключевое преимущество NavigationStack — типобезопасная навигация. Вы определяете путь как массив конкретных типов (или NavigationPath для гетерогенных стеков) и регистрируете destination для каждого типа. Это исключает ошибки несоответствия типов и делает навигацию предсказуемой.

NavigationView deprecated в iOS 17. Apple рекомендует мигрировать на NavigationStack для всех новых проектов и при обновлении минимальной версии до iOS 16.

NavigationPath — это тип, представляющий путь навигации в NavigationStack. Он может хранить гетерогенные значения (AnyHashable) или использоваться с конкретным типом через Binding к массиву [T: Hashable]. NavigationPath автоматически кодируется и декодируется для сохранения состояния.

swift
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 и создаёт экран.

swift
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 для маршрутизации. Это обеспечивает единый источник истины для всех возможных переходов в приложении.

swift
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?

NavigationStack — навигационный контейнер SwiftUI (iOS 16+), управляющий стеком экранов через типобезопасный путь. Заменил NavigationView, предлагая поддержку deep linking, программной навигации и сохранения состояния.

Чем NavigationStack отличается от NavigationView?

NavigationStack использует типобезопасный путь (NavigationPath) вместо прямого связывания NavigationLink с destination. Поддерживает программную навигацию, deep linking и Codable для сохранения состояния. Работает на SwiftUI, а не через UINavigationController.

Как работает NavigationPath?

NavigationPath — это тип, представляющий последовательность экранов в стеке. Вы добавляете значения в путь через path.append() или через NavigationLink с value. Для отображения каждого типа регистрируется .navigationDestination. NavigationPath поддерживает Codable и автоматическое сохранение состояния.

Как реализовать deep linking с NavigationStack?

Через программное управление путём: после обработки URL вызывайте path.append() с соответствующим значением маршрута. NavigationStack автоматически отобразит целевой экран. Возврат на корень — path.removeLast(path.count).

Нужно ли мигрировать с NavigationView на NavigationStack?

Да, если ваша минимальная версия iOS 16+. NavigationView deprecated в iOS 17. Миграция даёт типобезопасную навигацию, поддержку deep linking и лучшее соответствие SwiftUI lifecycle. Для проектов с iOS 15 и ниже пока используйте NavigationView.

Итоги

  • NavigationStack — современный навигационный контейнер SwiftUI (iOS 16+)
  • NavigationPath — типобезопасный путь для управления стеком экранов
  • NavigationDestination — регистрация целевых View по типам данных
  • NavigationView deprecated в iOS 17, миграция рекомендована
  • Deep linking и программная навигация — встроенная поддержка
  • Enum Route — предпочтительный паттерн для организации навигации
  • Гетерогенный стек через NavigationPath с любыми Hashable типами

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также