skip to Main Content

The detail view / the destination of the NavigationSplitView does not change when selecting another item in the sidebar.

Given a Swift Data Model

@Model final class MyModel {
    var text: String
    init(text: String) {self.text = text}
}

And a NavigationSplitView, that should display all of those models plus an edit view

struct MyModelEditView: View {
    @Environment(.modelContext) var modelContext
    @State var myModel: MyModel
    var body: some View {
       TextField("Edit Text", text: $myModel.text)
    }
}
 
struct ContentView: View {
    @Environment(.modelContext) var modelContext
    @Query var models: [MyModel]
    var body: some View {
        NavigationSplitView {
           List {
               ForEach(myModels) { model in
                   NavigationLink {
                      MyModelEditView(myModel: model)
                          .modelContext(modelContext)
                   } label: {
                       Text(model.text)
                   }
               }
           }
        } detail: {
            Text("Tap plus for a new model")
        }
        // we pretend that here is a button in a toolbar that adds a new model
    }       

When initially clicking on any item in the list, the detail view for this item is displayed! But when clicking on another model, the detail view just stays where it was, with the previous model.

I also tried to create an
@State var selectedModel: MyModel
and using the List initializer List(selection: $selectedModel) , tagging each item in the ForEach (.tag(model)) instead of using NavigationLink and then in the detail block:

detail: {
    if let selectedModel {
        MyModelEditView(myModel: myModel)
            .modelContext(modelContext)
    } else {
Text("Tap plus for a new model")
    }
}


But also this did not help.

I’ve found out that .navigationDestination(for:) is limited to NavigationStack, so I am left with no idea how to make a working Navigation Stack for my SwiftData models.

! Note that this is not specific to SwiftData models as objects, this does also happen with any Kind of structs or classes, I tried it with a struct containing just a single String such as the MyModel class.

2

Answers


  1. Chosen as BEST ANSWER

    I've found the fix after a lot of trial and error. The solution was simply to do this in the List:

    ForEach(myModels) { model in 
        NavigationLink {
            MyModelEditView(myModel: model)
                .modelContext(modelContext)
                .id(model.id) // <-- added .id(model.id)
        } label: {
            Text(model.text)
        }
    }
    

    As of what I have read, .id forces the detail view to be drawn again every time.

    I am not sure if this is the most elegant way of doing this, but it works.


  2. As suggested in the comments, @Bindable will fix the issue without having to use another view modifier.

    struct MyModelEditView: View {
        @Bindable var myModel: MyModel
        var body: some View {
           TextField("Edit Text", text: $myModel.text)
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search