Labs ICT
Pro Login

Navigation in SwiftUI

Moving between screens.

NavigationStack

NavigationStack replaced NavigationView in iOS 16. It's a container that manages a stack of views. Users push views onto the stack and pop back. You wrap your content in it and navigation just works.

struct ContentView: View {
    var body: some View {
        NavigationStack {
            List {
                NavigationLink("Item 1", destination: DetailView(title: "Item 1"))
                NavigationLink("Item 2", destination: DetailView(title: "Item 2"))
                NavigationLink("Item 3", destination: DetailView(title: "Item 3"))
            }
            .navigationTitle("My List")
        }
    }
}

struct DetailView: View {
    let title: String
    var body: some View {
        Text(title)
            .font(.largeTitle)
            .navigationTitle(title)
    }
}
Try it Yourself ->

NavigationLink

NavigationLink triggers navigation when tapped. It takes a label and a destination. You can use it inline in a list row or as a standalone button anywhere in your view hierarchy.

NavigationStack {
    VStack(spacing: 20) {
        NavigationLink("Go to Settings") {
            SettingsView()
        }
        .buttonStyle(.borderedProminent)

        NavigationLink {
            ProfileView()
        } label: {
            HStack {
                Image(systemName: "person.circle")
                Text("View Profile")
            }
            .padding()
            .background(Color.blue)
            .foregroundColor(.white)
            .cornerRadius(10)
        }
    }
    .navigationTitle("Home")
}
Try it Yourself ->

NavigationPath for Programmatic Navigation

Want to push views from code instead of user taps? Use NavigationPath. Create a binding, append values, and SwiftUI handles the rest. This is great for onboarding flows or deep linking.

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

    var body: some View {
        NavigationStack(path: $path) {
            VStack {
                Button("Go to Screen 2") {
                    path.append("Screen2")
                }
                Button("Go to Screen 3") {
                    path.append("Screen3")
                }
            }
            .navigationTitle("Home")
            .navigationDestination(for: String.self) { value in
                Text("Navigated to \(value)")
                    .font(.largeTitle)
            }
        }
    }
}
Try it Yourself ->

navigationTitle

Set the title of the navigation bar with .navigationTitle. It appears at the top of the screen and automatically adjusts for large title styles on supported devices.

NavigationStack {
    List(1..<20) { i in
        Text("Row \(i)")
    }
    .navigationTitle("Settings")
    // Large title on scroll, compact when scrolled
}
Try it Yourself ->

.toolbar for Customizing the Nav Bar

Add buttons and controls to the navigation bar with .toolbar. Place items in the principal, leading, or trailing position. This keeps your main content area clean.

NavigationStack {
    Text("Content")
        .navigationTitle("My App")
        .toolbar {
            ToolbarItem(placement: .navigationBarTrailing) {
                Button("Edit") { }
            }
            ToolbarItem(placement: .navigationBarLeading) {
                Button("Menu") { }
            }
            ToolbarItem(placement: .principal) {
                Text("Centered")
                    .font(.headline)
            }
        }
}
Try it Yourself ->

Sheet and fullScreenCover

Modals are perfect for focused tasks. sheet presents a view that users can swipe down to dismiss. fullScreenCover takes over the entire screen. Both are triggered by a boolean binding.

struct ModalExample: View {
    @State private var showingSheet = false
    @State private var showingFullScreen = false

    var body: some View {
        VStack(spacing: 20) {
            Button("Show Sheet") { showingSheet = true }
            Button("Show Full Screen") { showingFullScreen = true }
        }
        .sheet(isPresented: $showingSheet) {
            Text("Sheet Content")
                .presentationDetents([.medium])
        }
        .fullScreenCover(isPresented: $showingFullScreen) {
            Text("Full Screen Content")
                .font(.largeTitle)
                .toolbar {
                    ToolbarItem(placement: .navigationBarLeading) {
                        Button("Close") { showingFullScreen = false }
                    }
                }
        }
    }
}
Try it Yourself ->