Alle Beiträge

SwiftUI Navigation mit NavigationStack – der komplette Guide

SwiftUI Navigation mit NavigationStack – der komplette Guide

Warum NavigationView nicht mehr reicht

Wenn du mit SwiftUI anfängst, stolperst du schnell über Navigation. Bis iOS 16 war NavigationView der Standard — aber es hatte Probleme: kein programmatisches Navigieren, kein Deep Linking, und auf dem iPad verhielt es sich unvorhersehbar.

NavigationStack löst all das. Es ist seit iOS 16 verfügbar und sollte in jedem neuen Projekt verwendet werden. NavigationView ist deprecated — gewöhn dich gar nicht erst daran.

Die Grundlagen: NavigationStack und NavigationLink

Die einfachste Navigation sieht so aus:

struct ContentView: View {
    var body: some View {
        NavigationStack {
            List {
                NavigationLink("Profil", value: "profil")
                NavigationLink("Einstellungen", value: "settings")
            }
            .navigationTitle("Menü")
            .navigationDestination(for: String.self) { value in
                DetailView(name: value)
            }
        }
    }
}

struct DetailView: View {
    let name: String

    var body: some View {
        Text("Seite: \(name)")
            .navigationTitle(name)
    }
}

Wichtig: NavigationLink mit value: funktioniert nur zusammen mit .navigationDestination(for:). Das ist Absicht — es trennt das „Wohin" vom „Was wird angezeigt".

Programmatische Navigation mit NavigationPath

Der große Vorteil von NavigationStack gegenüber NavigationView: Du kannst den Navigation-Stack als State verwalten und programmatisch Screens pushen oder poppen.

struct ContentView: View {
    @State private var path = NavigationPath()

    var body: some View {
        NavigationStack(path: $path) {
            VStack(spacing: 20) {
                Button("Direkt zu Detail") {
                    path.append("detail-screen")
                }

                Button("Zurück zum Start") {
                    path.removeLast(path.count)
                }
            }
            .navigationTitle("Start")
            .navigationDestination(for: String.self) { value in
                Text(value)
            }
        }
    }
}

NavigationPath ist ein typen-löschender Container. Du kannst verschiedene Typen hineinwerfen — Strings, Integers, eigene Modelle — solange sie Hashable sind.

Mehrere Typen in einem Stack

In der Praxis navigierst du nicht nur zu Strings. So unterstützt du mehrere Typen:

struct ContentView: View {
    @State private var path = NavigationPath()

    var body: some View {
        NavigationStack(path: $path) {
            List {
                NavigationLink("Artikel", value: Article(title: "SwiftUI"))
                NavigationLink("Kategorie", value: Category(name: "iOS"))
            }
            .navigationDestination(for: Article.self) { article in
                ArticleDetailView(article: article)
            }
            .navigationDestination(for: Category.self) { category in
                CategoryView(category: category)
            }
        }
    }
}

Jeder Typ bekommt sein eigenes .navigationDestination. SwiftUI wählt automatisch das richtige.

Deep Linking

Mit programmatischer Navigation ist Deep Linking trivial. Wenn deine App eine URL wie myapp://article/swift-basics empfängt, musst du nur den passenden Wert auf den Stack pushen:

.onOpenURL { url in
    if let slug = url.pathComponents.last {
        path.append(slug)
    }
}

Das funktioniert auch für Push-Notifications, Spotlight-Ergebnisse und Widgets — alles, was einen bestimmten Screen öffnen soll.

iPad: NavigationSplitView

Auf dem iPad willst du keine Push-Navigation, sondern ein Sidebar-Layout. Dafür gibt es NavigationSplitView:

struct iPadView: View {
    @State private var selection: String?

    var body: some View {
        NavigationSplitView {
            List(selection: $selection) {
                Label("Newsroom", systemImage: "newspaper")
                    .tag("newsroom")
                Label("Einstellungen", systemImage: "gear")
                    .tag("settings")
            }
            .navigationTitle("Menü")
        } detail: {
            switch selection {
            case "newsroom":
                Text("Newsroom")
            case "settings":
                Text("Einstellungen")
            default:
                Text("Wähle einen Bereich")
            }
        }
    }
}

Tipp: Prüfe mit @Environment(\.horizontalSizeClass), ob du auf iPhone oder iPad bist, und zeige die passende Navigation:

@Environment(\.horizontalSizeClass) private var sizeClass

var body: some View {
    if sizeClass == .regular {
        iPadLayout
    } else {
        iPhoneLayout
    }
}

Häufige Fehler

FehlerLösung
<code>NavigationView</code> verwendenImmer <code>NavigationStack</code> oder <code>NavigationSplitView</code>
<code>NavigationLink(destination:)</code> statt <code>value:</code><code>value:</code> + <code>.navigationDestination</code> ist die moderne API
Verschachtelte <code>NavigationStack</code>Nur <strong>einen</strong> <code>NavigationStack</code> pro Hierarchie — nicht in jeder Subview einen neuen
<code>.navigationTitle</code> außerhalb des StacksDer Modifier gehört auf die <strong>innere</strong> View, nicht auf <code>NavigationStack</code> selbst
Eigene Back-ButtonsLass den System-Back-Button. Nutzer erwarten ihn, und er funktioniert mit Swipe-Back

Fazit

NavigationStack ist die Grundlage jeder modernen SwiftUI-App. Die Trennung von Navigation-State und View-Darstellung macht Deep Linking, programmatische Navigation und Testbarkeit deutlich einfacher als mit dem alten NavigationView. Fang von Anfang an damit an — ein späterer Umbau ist aufwändig.

Mehr über unsere App-Entwicklung →


Dieser Artikel wurde zuletzt am 20. März 2026 aktualisiert. Getestet mit Xcode 26 / iOS 26 / Swift 6.

Beitrag teilen