Home > Back-end >  SwiftUI Navigation Stack - How to pass a navigation title to destination
SwiftUI Navigation Stack - How to pass a navigation title to destination

Time:12-18

Right now I'm learning NavigationStacks in SwiftUI and I'm wondering if there is a elegant solution for passing the title of the NavigationLink from the previous view.

Consider the following example:

struct Foo: Hashable, Identifiable {
    let id = UUID().uuidString
    let name: String
    let bar: [String]
}

let exampleData: [Foo] = [
    Foo(name: "Item 1", bar: ["Lorem", "ipsum"]),
    Foo(name: "Item 2", bar: ["Lorem2", "ipsum2"])
]

struct MyNavigationView: View {
    @State private var data = exampleData
    var body: some View {
        NavigationView {
            List(data) { d in
                NavigationLink(destination: {
                    List{
                        ForEach(d.bar, id: \.self) { val in
                            Text(val)
                        }
                        
                    }.navigationTitle(d.name)
                }) {
                    Text(d.name)
                }
            }
        }
    }
}

I created a NavigationStack and would like the destination to have d.name as its title:

struct MyNavigationStackView: View {
    @State private var data = exampleData
    
    var body: some View {
        NavigationStack {
            List(data) { d in
                NavigationLink(value: d.bar) {
                    Text(d.name)
                }
            }
            .navigationDestination(for: Foo.self) { elements in
                // I cannot use Foo.self as a destination since it's already used for a different view
            }
            .navigationDestination(for: [String].self) { elements in
                List(elements, id: \.self) { e in
                    Text(e)
                } // <- I would like this list to have d.name as a navigationTitle like the NavigationView has
            }
        }
    }
}

Is there some clever way to solve this without modifying my model?

CodePudding user response:

The solution can come by wrapping Foo into an auxiliary struct, let's call it WrappedFoo. When you pass the auxiliary struct, you unwrap it into the value of Foo, and read the properties one by one.

Like this...

This is the auxiliary struct:

struct WrappedFoo: Hashable {

    // It contains only one property of type Foo
    let foo: Foo
    
    init(_ foo: Foo) {
        self.foo = foo
    }
}

How to use it:

var body: some View {
    NavigationStack {
        List(data) { d in

            // Pass the auxiliary struct
            NavigationLink(value: WrappedFoo(d)) {
                Text(d.name)
            }
        }
        .navigationDestination(for: Foo.self) { elements in
            // I cannot use Foo.self as a destination since it's already used for a different view
        }

        // Use the auxiliary struct and get the value of the Foo property
        .navigationDestination(for: WrappedFoo.self) { wFoo in
            List(wFoo.foo.bar, id: \.self) { e in
                Text(e)
            }
            
            // "Unwrap" Foo to get the name
            .navigationTitle(wFoo.foo.name)
        }
    }
}
  • Related