NavigationLink pops out upon List update ONLY when List is not scrolled to the top
Solution 1:
You're correct that this relates to the fact that the List
lazily loads elements -- once the NavigationLink
is off the screen, if the Chat
element changes, the View
ends up getting popped off the stack.
The standard solution to this is to add a hidden NavigationLink
to your hierarchy that has an isActive
property that controls whether or not it is active or not. Unfortunately, it requires a little more boilerplate code than the convenient list element binding that was introduced in Swift 5.5.
Your code might look something like this:
struct DebugView: View {
@StateObject var chatsManager = ChatsManager()
@State private var activeChat : String?
private func activeChatBinding(id: String?) -> Binding<Bool> {
.init {
activeChat != nil && activeChat == id
} set: { newValue in
activeChat = newValue ? id : nil
}
}
private func bindingForChat(id: String) -> Binding<Chat> {
.init {
chatsManager.chats.first { $0.id == id }!
} set: { newValue in
chatsManager.chats = chatsManager.chats.map { $0.id == id ? newValue : $0 }
}
}
var body: some View {
NavigationView{
VStack{
HStack {
Text("Chats")
}.padding()
VStack{
List() {
ForEach($chatsManager.chats, id: \.id) { $chat in
Button(action: {
activeChat = chat.id
}) {
DemoChatRow(chat: $chat)
}
}
}.listStyle(.plain)
}
.background {
NavigationLink("", isActive: activeChatBinding(id: activeChat)) {
if let activeChat = activeChat {
ChatDetailView(chat: bindingForChat(id: activeChat).wrappedValue)
} else {
EmptyView()
}
}
}
}.navigationBarTitle("").navigationBarHidden(true)
}.navigationViewStyle(.stack)
.environmentObject(chatsManager)
}
}
Note: I kept the Binding
that you have to DemoChatRow
even though it looks like it's just a one-way connection here in the demo code, making the assumption that in your real code, you need two-way communication there