TruMan1
TruMan1

Reputation: 36158

How to use NavigationLink outside of NavigationStack?

I have a safe area inset view that is placed on my NavigationStack so that acts as a bottom bar across all pushed views. However, in the safe area inset view, there are button which I'd like to push views onto the stack. The NavigationLink is greyed out though since it is outside of a NavigationStack.

This is what my code looks like:

NavigationStack {
    VStack {
        Image(systemName: "globe")
        Text("Hello, world!")
    }
    .toolbar {
        ToolbarItem(placement: .navigationBarLeading) {
            NavigationLink("Button 1") {
                Text("Screen 1")
            }
        }
        ToolbarItem(placement: .navigationBarTrailing) {
            NavigationLink("Button 2") {
                Text("Screen 2")
            }
        }
    }
}
.safeAreaInset(edge: .bottom, spacing: 0) {
    HStack {
        NavigationLink("Button 1") {
            Text("Screen 1")
        }
        .frame(minWidth: 0, maxWidth: .infinity)
        NavigationLink("Button 2") {
            Text("Screen 2")
        }
        .frame(minWidth: 0, maxWidth: .infinity)
    }
    .padding()
    .background(.regularMaterial)
}

It behaves in a way where the bottom view does indeed stick across all pushed views, but the buttons there do not do anything (the ones in the top navigation bar work properly though):

enter image description here

How can I get the buttons in the bottom safe area inset to navigation to different screens while still keeping it on the NavigationStack level since I do not want to place this bottom overlay code on each sub view?

Upvotes: 1

Views: 922

Answers (1)

ChrisR
ChrisR

Reputation: 12165

You can use programmatic navigation with NavigationStack:

The state var navigationPath holds the active path and you can change it either with NavigationLink(value:) or by setting it directly (like in the safe area buttons).

struct ContentView: View {
    
    // holds the active navigation path
    @State private var navigationPath: [Int] = []
    
    var body: some View {
        
        NavigationStack(path: $navigationPath ) {
            VStack {
                Image(systemName: "globe")
                Text("Hello, world!")
            }
            // defines destinations of path
            .navigationDestination(for: Int.self, destination: { value in
                switch value {
                case 1: Text("Screen 1")
                case 2: Text("Screen 2")
                default: Text("unknown view")
                }
            })
            
            .toolbar {
                ToolbarItem(placement: .navigationBarLeading) {
                    NavigationLink("Button 1", value: 1)
                }
                ToolbarItem(placement: .navigationBarTrailing) {
                    NavigationLink("Button 2", value: 2)
                }
            }
        }
        
        .safeAreaInset(edge: .bottom, spacing: 0) {
            HStack {
                Button("Button 1") {
                    navigationPath = [1]
                    // or if you want to extend the path:
                    // navigationPath.append(1)
                }
                .frame(maxWidth: .infinity)
                Button("Button 2") {
                    navigationPath = [2]
                    // or if you want to extend the path:
                    // navigationPath.append(2)
               }
                .frame(maxWidth: .infinity)
            }
            .padding()
            .background(.regularMaterial)
        }
    }
}

Upvotes: 1

Related Questions