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. За разлика от NavigationView, който разчиташе на UINavigationController под капака, NavigationStack е изцяло имплементиран в SwiftUI, което подобрява предвидимостта и съвместимостта с жизнения цикъл на SwiftUI.

Основна структура на NavigationStack

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

Ключово предимство на NavigationStack — типобезопасна навигация. Определяте пътя като масив от конкретни типове (или NavigationPath за хетерогенни стекове) и регистрирате destination за всеки тип. Това елиминира грешките от несъвместимост на типове и прави навигацията предвидима.

NavigationView е остарял в 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 известия, дълбоки връзки и възстановяване на навигацията след рестарт.

Модел 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 е остарял в iOS 17. Миграцията дава типобезопасна навигация, поддръжка на deep linking и по-добро съответствие с жизнения цикъл на SwiftUI. За проекти с iOS 15 и по-ниски все още използвайте NavigationView.

Обобщение

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

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

IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.

Обсъдете проекта

Прочетете също