0

It seems so weird that I couldn't find anything. Isn't this a common problem?

So, I have a list in a NavigationView/-Stack and have a .toolbar add button. When tapping that button it should open a DetailView as a navigation detail view (not a .sheet) to allow me to edit the new item, and also add it to the list. There seems to be navigationdestination(item:destination:) which looks like what I want but that's beta iOS 17+.

Any ideas?

struct Item: Identifiable, Hashable {
    let id = UUID()
    var title: String = ""
}

struct EditView: View {
    @Binding var item: Item
    var body: some View {
        TextField("Edit", text: $item.title)
    }
}

struct ListView: View {
    @State var todos = [Item(title: "abc")]
    @State var path = NavigationPath()
    
    var body: some View {
        NavigationStack(path: $path) {
            List($todos) { $todo in
                NavigationLink(todo.title) {
                    EditView(item: $todo)
                }
            }
//            .navigationDestination(for: Item.self, destination: { item in
//                // ERR: item is not a Binding
//                EditView(item: item)
//            })
            .toolbar {
                Button("+") {
                    let new = Item()
                    todos.append(new)
                    // path.append(todos.last) ??
                }
            }
        }
    }
}

This is my code. So I got the navigation link working for the list but am not sure how to also make it so it programmatically opens the navigation view when adding a new item.

  • 1
    `NavigationView` is deprecated, you should use `NavigationStack` which will give you control of the link programmatically. See: https://developer.apple.com/documentation/swiftui/navigationlink#Control-a-presentation-link-programmatically – workingdog support Ukraine Aug 23 '23 at 21:21
  • Thanks. I updated the code. But I still couldn't find a way to achieve what I'm looking for. Again, the only thing coming close to it is the beta function I mentioned. – Emily Axford Aug 23 '23 at 21:46

1 Answers1

0

Try this approach, using a NavigationPath with the NavigationStack, as shown, works for me. This code will ...make it so it programmatically opens the navigation view when adding a new item.

struct ContentView: View {
    var body: some View {
        ListView()
    }
}

struct ColorDetail: View {
    var color: Color
    var body: some View {
        Text(color.description)
    }
}

struct ListView: View {
    @State var colors: [Color] = [.red] // <--- here all colors must be unique
    @State var path = NavigationPath()  // <-- here
    
    var body: some View {
        NavigationStack(path: $path) {  // <-- here
            List(colors, id: \.self) { color in   // <-- here
                NavigationLink(color.description, value: color)
            }
            .toolbar {
                Button("+") {
                    let newColor = Color.blue  // <-- here, a different color
                    colors.append(newColor)
                    path.append(newColor)  // <-- here
                }
            }
            .navigationDestination(for: Color.self) { color in
                ColorDetail(color: color)
            }
            .navigationTitle("Colors")
        }
    }
}

EDIT-1:

With regards to your new question, here is the code that will allow you to change the Color in the ColorDetail.

struct ContentView: View {
    var body: some View {
        ListView()
    }
}

struct ColorDetail: View {
    @Binding var mycolor: MyColor
    
    var body: some View {
        Text(mycolor.color.description)
        Picker("", selection: $mycolor.color) {
            ForEach([Color.black, Color.pink, Color.yellow, Color.green], id: \.self) { color in
                Text(color.description).tag(color)
            }
        }
    }
}

struct MyColor: Identifiable, Hashable {
    let id = UUID()
    var color: Color
}

struct ListView: View {
    @State var myColors: [MyColor] = [MyColor(color: .red)]
    @State var path = NavigationPath()
    
    var body: some View {
        NavigationStack(path: $path) {
            List(myColors) { color in
                NavigationLink(color.color.description, value: color)
            }
            .toolbar {
                Button("+") {
                    let newColor = MyColor(color: .blue)
                    myColors.append(newColor)
                    path.append(newColor)
                }
            }
            .navigationDestination(for: MyColor.self) { color in
                if let ndx = myColors.firstIndex(where: {$0.id == color.id}) {
                    ColorDetail(mycolor: $myColors[ndx])
                }
            }
            .navigationTitle("Colors")
        }
    }
}
  • Thanks again, I think it did help getting a little closer, but I'm still not able to edit the actual value. If I see correctly the color passed as the path, the color in the detail view is just a copy. What I want is a Binding on the actual new value so I can edit it. Sorry if that wasn't clear before.. but thanks again! – Emily Axford Aug 23 '23 at 23:10
  • Updated my answer with a solution to your new question/requirements. – workingdog support Ukraine Aug 23 '23 at 23:49
  • Amazing, it works perfectly! TYSM! I'm not a huge fan of `if let ndx = myColors.firstIndex` but all things considered it's probably the best solution. thx again.. – Emily Axford Aug 24 '23 at 00:12