Labs ICT
⭐ Pro Login

Navigation in SwiftUI

Moving between screens.

3 min read | Swift Tutorial
⭐

Want the full learning experience?

Get structured courses, certificates, projects, and instructor support with LabsICT Pro.

Explore Pro Courses

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)
    }
}

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")
}

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)
            }
        }
    }
}

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
}

.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)
            }
        }
}

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 }
                    }
                }
        }
    }
}