当父视图更新时,NavigationLink 页面会自动关闭

问题描述 投票:0回答:1

我正在尝试使用 SwiftUI 和 Firebase 创建一个聊天应用程序。我当前的方法涉及使用聊天室文档,并且每个聊天室中都有一个消息子集合。

因此,我使用两种不同的模型来获取聊天室数据和消息数据。首先,我获取有关聊天室的数据以进行列表视图。一旦用户单击其中一个聊天,就会触发一个 NavigationLink,将他们带到聊天的详细信息,也就是当我获取消息和数据时。

我想做的是跟踪发送的最后一条消息,以便可以从聊天室列表中看到它。目前这是通过从 NavigationLink 内部更新聊天室模型来完成的。由于我对来自 firebase 的数据有一个实时侦听器,这会触发父视图的更新,从而重新加载并关闭 NavigationLink。

我期望的行为是即使父视图更新也不要关闭 NavigationLink。

我非常感谢任何帮助。

家长视图

struct ChatListView: View {
var group: Group

@EnvironmentObject var sessionStore: SessionStore
@EnvironmentObject var groupModel: GroupModel

@ObservedObject var chatroomModel = ChatRoomModel()
@State var joinModal = false

init(group: Group) {
    self.group = group
    
    chatroomModel.fetchData(groupID: group.id)
}

var body: some View {
    NavigationView {
        ZStack {
            BackgroundView()
            
            VStack {
                ScrollView {
                    ForEach(chatroomModel.chatrooms) { chatroom in
                        NavigationLink (destination: ChatMessagesView(chatroom: chatroom, chatroomModel: chatroomModel)) {
                    (...)
                    }
                }
                .padding()
                .frame(maxWidth: .infinity)
            }
            
        }
        .navigationBarTitle("Chats")
    }

子视图

struct ChatMessagesView: View {
let chatroom: ChatRoom
let chatroomModel: ChatRoomModel
@EnvironmentObject var sessionStore: SessionStore


@ObservedObject var messagesModel = MessagesModel()

@State var messageField = ""

init (chatroom: ChatRoom, chatroomModel: ChatRoomModel) {
    self.chatroom = chatroom
    self.chatroomModel = chatroomModel
    messagesModel.fetchData(documentID: self.chatroom.id ?? "erflwekrjfne")
}

var body: some View {
    GeometryReader { geo in
        
        VStack {
            
            CustomScrollView(scrollToEnd: true) {
                LazyVStack (spacing: 20){
                    ForEach(messagesModel.messages) { message in
                        MessageView(message: message)
                    }
                }
            }
            .padding()

            
            HStack {
                TextField("Type here...", text: $messageField)
                    .textFieldStyle(TextInputStyle())
                    .padding(EdgeInsets(top: 0, leading: 10, bottom: 0, trailing: 10))
                
                Button(action: {
                    if messageField != "" {
                        messagesModel.sendMessage(content: messageField, documentID: chatroom.id!, displayName: sessionStore.session?.firstName)

                        * Updates parent view and triggers problem *
                        chatroomModel.updateChatroom(id: chatroom.id!, fields: ["lastMessage": messageField, "lastSender": sessionStore.session?.firstName ?? "Anon"])
                        
                        messageField = ""
                    }
                }, label: {
                    HStack {
                        Image(systemName: "paperplane.circle")
                            .resizable()
                            .frame(width: 50, height: 50, alignment: .center
                    }
                })
            }
            .padding(EdgeInsets(top: 0, leading: 10, bottom: 10, trailing: 10))
        }
        .navigationBarTitle(chatroom.title ?? "Chat", displayMode: .inline)
        
    }
}

}

swift firebase google-cloud-firestore swiftui swiftui-navigationlink
1个回答
0
投票

如果您使用
ForEach

,请确保您拥有唯一标识符

将独特的

id
设置为您的
ForEach
循环:

ForEach(msgViewModel.msgs.sorted(by: {$0.key < $1.key}), id:\.self.id) {
. . .
}

如果您将列表包装在

Array
中,那么您的 id 将是
\.element.id
:

ForEach(Array(msgViewModel.msgs.enumerated()), id: \.element.id) { index, page in
. . .
}

对我来说,这固定地解雇了第一个孩子。如果您只有一个孩子,那么这可能就是您所需要的。

如果您有进一步嵌套的NavigationLink

.isDetailLink(false) 修饰符添加到

NavigationLink

这是修复后的演示代码:

import SwiftUI

@main
struct UIExperimentApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

struct ContentView: View {
    @StateObject var msgViewModel = MsgViewModel()
    
    var body: some View {
        NavigationView {
            List {
                /// use this if you're wrapping the list in `Array`:  ForEach(Array(msgViewModel.msgs.enumerated()), id: \.element.id) { index, page in
                ForEach(msgViewModel.msgs, id:\.self.id) { msg in
                    NavigationLink(destination: ChildOneView(msgViewModel: msgViewModel, msg: msg)) {
                        Text("Msg read: \(msg.read ? "Yes" : "No")")
                            .padding()
                    }
                    .isDetailLink(false)
                }
            }
        }
    }
}

struct ChildOneView: View {
    @ObservedObject var msgViewModel: MsgViewModel
    var msg: Msg
    
    var body: some View {
        VStack {
            Text("Child One Message: \(msg)")
            
            NavigationLink(destination: ChildTwoView(msgViewModel: msgViewModel, msg: msg)) {
                Text("Go two Child two")
                    .padding()
                    .background(Color.blue)
                    .foregroundColor(.white)
                    .cornerRadius(8)
            }
            .isDetailLink(false)
        }
    }
}

struct ChildTwoView: View {
    @ObservedObject var msgViewModel: MsgViewModel
    var msg: Msg
    
    var body: some View {
        VStack {
            Text("Child two: Message \(msg)")
                .padding(40)
            
            Button("Mark as Read") {
                if let index = msgViewModel.msgs.firstIndex(where: { $0.id == msg.id }) {
                    msgViewModel.msgs[index].read.toggle()
                }
            }
            .padding()
            .background(Color.blue)
            .foregroundColor(.white)
            .cornerRadius(8)
        }
    }
}

class MsgViewModel : ObservableObject {
    @Published var msgs = [
        Msg(id: "id1", read:false, content: "Hello"),
        Msg(id: "id2", read:false, content: "World")
    ]
}

struct Msg {
    var id : String
    var read = false
    var content : String
}

对我有帮助的链接:

  1. 嵌套导航链接在最新的 SwiftUI 中无法正常工作
  2. 已解决:使用 NavigationLink 和 isActive 弹出多个嵌套视图
  3. 关闭导航视图中的多个视图
  4. 在 SwiftUI、iOS15、二级 NavigationLink 中,isActive 不起作用
© www.soinside.com 2019 - 2024. All rights reserved.