I would like to build a simple List in SwiftUI of names that, when tapped, navigate to a detail view that allows modification of those names.
When I use a @State array to supply names to the List, and attempt to pass a binding to an element in the array to a detail view, any modification of the bound object in the detail view causes a redraw of not only the detail view, but also the offscreen view containing the List.
Generally, what seems to be the problem is that it's not possible to mutate data that is in a parent view. This is described in this post on the Apple Developer Forums.
There is a post with a similar title on this subject here at StackOverflow as well as blog posts that attempt to work around the issue, etc., but I have not found a good general-purpose solution or design pattern that deals with something that seems like a fairly common use-case.
Apple's WWDC 2022 video "The SwiftUI Cookbook for Navigation" completely sidesteps the issue of mutating data, and shows only examples of an app with static data (i.e. they don't modify any recipes).
Here is some code I've written to demonstrate the problem:
//
// ContentView.swift
//
import SwiftUI
struct Item: Hashable, Identifiable {
let id = UUID()
var text: String
}
struct ContentView: View {
@State var items = [Item(text: "foo bar"), Item(text: "biz boz") ]
var body: some View {
NavigationStack {
let _ = { print("render NavigationStack") }()
List(items) { item in
let _ = { print("render List item") }()
NavigationLink(item.text, value: item.id)
}
.navigationDestination(for: UUID.self) { id in
if let index = items.firstIndex(where: {$0.id == id}) {
let _ = { print("render TextField") }()
VStack {
TextField("Value", text: $items[index].text)
.multilineTextAlignment(.center)
Spacer()
}
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
and here is a video showing the problem:
Notice that when a character is typed into the text field the cursor always jumps to the end of the field. This is because the entire detail view with the TextField is being re-rendered because its parent view is being re-rendered when the text is modified.
What I'm specifically trying to accomplish is to separate the redraw of the parent view from data modifications made in the detail view. It would be ideal if the parent view wouldn't redraw at all while offscreen, but I still need the list in the parent view to update its text based on modifications made in the detail view. I'd also like to pass a binding to the detail view if possible, but at this point would be happy with any functional workaround.

BindingandnavigationDestinationare not compatible.