我正在尝试使用 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)
}
}
}
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
. . .
}
对我来说,这固定地解雇了第一个孩子。如果您只有一个孩子,那么这可能就是您所需要的。
将 .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
}