skip to Main Content

I am passing a Person binding from the first view to the second view to the third view, when I update the binding value in the third view it pops back to the second view, I understand that SwiftUI updates the views that depend on the state value, but is poping the current view is the expected behavior or I am doing something wrong?

struct Person: Identifiable {
    let id = UUID()
    var name: String
    var numbers = [1, 2]
}
struct FirstView: View {
    @State private var people = [Person(name: "Current Name")]
    var body: some View {
        NavigationView {
            List($people) { $person in
                NavigationLink(destination: SecondView(person: $person)) {
                    Text(person.name)
                }
            }
        }
    }
}
struct SecondView: View {
    @Binding var person: Person
    var body: some View {
        Form {
            NavigationLink(destination: ThirdView(person: $person)) {
                Text("Update Info")
            }
        }
    }
}
struct ThirdView: View {
    @Binding var person: Person
    var body: some View {
        Form {
            Button(action: {
                person.numbers.append(3)
            }) {
                Text("Append a new number")
            }
        }
    }
}

2

Answers


  1. SwiftUI works by updating the rendered views to match what you have in your state.

    In this case, you first have a list that contains an element called Current Name. Using a NavigationLink you select this item.

    You update the name and now that previous element no longer exists, it’s been replaced by a new element called New Name.

    Since Current Name no longer exists, it also cannot be selected any longer, and the view pops back to the list.

    To be able to edit the name without popping back, you’ll need to make sure that the item on the list is the same, even if the name has changed. You can do this by using an Identifiable struct instead of a String.

    struct Person: Identifiable {
      let id = UUID().uuidString
      var name = "Current Name"
    }
    
    struct ParentView: View {
      @State private var people = [Person()]
      var body: some View {
        NavigationView {
          List($people) { $person in
            NavigationLink(destination: ChildView(person: $person)) {
              Text(person.name)
            }
          }
        }
      }
    }
    
    struct ChildView: View {
      @Binding var person: Person
      var body: some View {
        Button(action: {
          person.name = "New Name"
        }) {
          Text("Update Name")
        }
      }
    }
    
    Login or Signup to reply.
  2. When navigating twice you need to either use isDetailLink(false) or StackNavigationViewStyle, e.g.

    struct FirstView: View {
        @State private var people = [Person(name: "Current Name")]
        var body: some View {
            NavigationView {
                List($people) { $person in
                    NavigationLink(destination: SecondView(person: $person)) {
                        Text(person.name)
                    }
                    .isDetailLink(false) // option 1
                }
            }
            .navigationViewStyle(.stack) // option 2
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search