从套接字收到消息时视图未更新(在 SwiftUI 中构建聊天)

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

我尝试在 SwiftUI 中构建聊天,目前每次收到消息时我都会打印它以证明套接字实际上正在工作,但是尽管打印结果是正确的,但视图不会更新并且我看不到消息出现就在视图上。

我认为他们在定义我的状态时可能是一个错误?

这是我的代码

import SwiftUI
import SocketIO

struct ChatMessage: Hashable {
    var message: String
    var MatchImg: String
    var isMe: Bool

}

class ChatMessageList: ObservableObject {
    @Published var list: [ChatMessage]
    
    init(list: [ChatMessage]) {
        self.list = list
    }
    
    func addItem(item: ChatMessage) {
        list.append(item)
        objectWillChange.send()
    }
}

struct ChatRow : View {
    var chatMessage: ChatMessage
    var body: some View {
        if !chatMessage.isMe {
            
            HStack {
                Group {
                    Image(chatMessage.MatchImg)
                        .resizable()
                        .aspectRatio(contentMode: .fill)
                        .frame(width: 32, height: 32)
                        .clipShape(Circle())
                    Text(chatMessage.message)
                        .bold()
                        .padding(10)
                        .foregroundColor(Color.white)
                        .background(.purple)
                        .cornerRadius(10)
                }
            }
        }
        else {
            HStack {
                Group {
                    Spacer()
                    Text(chatMessage.message)
                        .bold()
                        .foregroundColor(Color.white)
                        .padding(10)
                        .background(.red)
                    .cornerRadius(10)
                }
            }
        }

    }
    }



struct MessageView: View {
    
    @StateObject var messages: ChatMessageList = ChatMessageList(list: [
        ChatMessage(message: "Hello world", MatchImg:"ye",isMe: true),
        ChatMessage(message: "Hello world", MatchImg:"ye",isMe: false)
    ])
    
    @State private var composedMessage: String = ""
    let roomID: String
    let userID: String
    let otherUser: String
    
    private var socket: SocketIOClient
    private var manager: SocketManager
    
    init(roomID: String, userID: String, otherUser: String) {
        self.roomID = roomID
        self.userID = userID
        self.otherUser = otherUser
        
        self.manager = SocketManager(socketURL: URL(string: "http://localhost:8080")!, config: [.log(false), .compress])
        self.socket = self.manager.defaultSocket
        
        self.socket.connect()
        
        self.addHandlers()
        
        
    }

    func addHandlers() {
        self.socket.on("message") { data, ack in
            print(data[0])
        }
        self.socket.on("chat message") { data, ack in
            let messageText = data[0]
            print("got message", messageText)


            let newMessage = ChatMessage(message: messageText as! String, MatchImg: "ye", isMe: false)
            addMessage(message: newMessage)
            
           
        }
    }
    
    func addMessage(message: ChatMessage) {
        DispatchQueue.main.async {
            messages.addItem(item: message)
        }
    }
    
    var body: some View {
        VStack(alignment: .center){
            HStack {
                Image("ye")
                    .resizable()
                    .aspectRatio(contentMode: .fill)
                    .frame(width: 32, height: 32)
                    .clipShape(Circle())
                Text("Kim Kardashiana")
                    .bold()
                    .padding(10)
                    .foregroundColor(Color.black)
                    .cornerRadius(10)
                
            }
            
            List{
                // we have several messages so we use the For Loop
                ForEach(messages.list, id: \.self) { msg in
                    ChatRow(chatMessage: msg)
                        .listRowSeparator(.hidden)
                        .listRowBackground(Color.clear)
                }
                
            }
            .padding(0)
            .listStyle(PlainListStyle())
            .environment(\.defaultMinListRowHeight, 0)
            
            
            
            
            HStack {
                // this textField generates the value for the composedMessage @State var
                TextField("Message...", text: $composedMessage).frame(minHeight: CGFloat(36))
                    .padding(.horizontal,12)
                    .overlay(
                        RoundedRectangle(cornerRadius: 15)
                            .stroke(Color.blue, lineWidth: 2)
                    )
                // the button triggers the sendMessage() function written in the end of current View
                Button("Send"){
                    Task {
                        self.socket.emit("message", roomID, composedMessage)
                        print(self.messages)
                        self.messages.addItem(item: ChatMessage(message: composedMessage, MatchImg: "ye", isMe: true))
                        print(self.messages)
                        self.composedMessage = ""
                    }
                }
                Button(action: {}){
                    Image("person.fill")
                        .foregroundColor(Color.black)
                    
                }
            }.padding(20)
            .task{
                print(self.socket.status)
                self.socket.emit("identify", userID)
                
                self.socket.emit("subscribe", roomID, otherUser)
            }
            
            // that's the height of the HStack
            
        }
    }
}
    struct MessageView_Previews: PreviewProvider {
        static var previews: some View {
            MessageView(roomID: "588b4e7a9bed4ece96ec6769fe6ade8a", userID: "74eae22fe84149af98f62e4376e7867a", otherUser: "ceb5e306b8ba4977bd5af5dfd77ca710")
        }
    }
sockets swiftui chat
1个回答
0
投票

尝试这种方法,使用

Identifiable
代替
ChatMessage
,这样
ForEach
元素根据需要是唯一的,并使用
ForEach(messages.list) { msg in ...}

测试代码示例:

struct ContentView: View {
    var body: some View {
        MessageView(roomID: "room-1", userID: "user-1", otherUser: "user-2")
    }
}

struct ChatMessage: Identifiable, Hashable { // <-- here
    let id = UUID()  // <-- here
     
    var message: String
    var MatchImg: String
    var isMe: Bool
}

class ChatMessageList: ObservableObject {
    @Published var list: [ChatMessage]
    
    init(list: [ChatMessage]) {
        self.list = list
    }
    
    func addItem(item: ChatMessage) {
        list.append(item)
        //objectWillChange.send()  // <-- here no need
    }
}

struct ChatRow : View {
    var chatMessage: ChatMessage
    var body: some View {
        if !chatMessage.isMe {
            
            HStack {
                Group {
                    Image(systemName: "globe") // <-- for testing
                   // Image(chatMessage.MatchImg)
                        .resizable()
                        .aspectRatio(contentMode: .fill)
                        .frame(width: 32, height: 32)
                        .clipShape(Circle())
                    Text(chatMessage.message)
                        .bold()
                        .padding(10)
                        .foregroundColor(Color.white)
                        .background(.purple)
                        .cornerRadius(10)
                }
            }
        }
        else {
            HStack {
                Group {
                    Spacer()
                    Text(chatMessage.message)
                        .bold()
                        .foregroundColor(Color.white)
                        .padding(10)
                        .background(.red)
                        .cornerRadius(10)
                }
            }
        }
        
    }
}

struct MessageView: View {
    
    @StateObject var messages: ChatMessageList = ChatMessageList(list: [
        ChatMessage(message: "Hello world1", MatchImg:"ye", isMe: true),
        ChatMessage(message: "Hello world2", MatchImg:"ye", isMe: false)
    ])
    
    @State private var composedMessage: String = ""
    let roomID: String
    let userID: String
    let otherUser: String
    
    //   private var socket: SocketIOClient
    //   private var manager: SocketManager
    
    
    init(roomID: String, userID: String, otherUser: String) {
        self.roomID = roomID
        self.userID = userID
        self.otherUser = otherUser
        
        //        self.manager = SocketManager(socketURL: URL(string: "http://localhost:8080")!, config: [.log(false), .compress])
        //        self.socket = self.manager.defaultSocket
        //
        //        self.socket.connect()
        
        self.addHandlers() // <-- todo need to fix
    }
    
    func addHandlers() {
        //        self.socket.on("message") { data, ack in
        //            print(data[0])
        //        }
        //        self.socket.on("chat message") { data, ack in
        //            let messageText = data[0]
        //            print("got message", messageText)
        //            let newMessage = ChatMessage(message: messageText as! String, MatchImg: "ye", isMe: false)
        //            addMessage(message: newMessage)
        //        }
        
        // for testing
        let newMessage = ChatMessage(message: UUID().uuidString, MatchImg: "ye", isMe: false)
        addMessage(message: newMessage)
    }
    
    func addMessage(message: ChatMessage) {
        DispatchQueue.main.async {
            messages.addItem(item: message)
        }
    }
    
    var body: some View {
        VStack(alignment: .center){
            HStack {
                Image(systemName: "house") // <--- for testing
                    .resizable()
                    .aspectRatio(contentMode: .fill)
                    .frame(width: 32, height: 32)
                    .clipShape(Circle())
                Text("Kim Kardashiana")
                    .bold()
                    .padding(10)
                    .foregroundColor(Color.black)
                    .cornerRadius(10)
            }
            List{
                // we have several messages so we use the For Loop
                ForEach(messages.list) { msg in  // <--- here
                    ChatRow(chatMessage: msg)
                        .listRowSeparator(.hidden)
                        .listRowBackground(Color.clear)
                }
            }
            .padding(0)
            .listStyle(PlainListStyle())
            .environment(\.defaultMinListRowHeight, 0)
            
            HStack {
                // this textField generates the value for the composedMessage @State var
                TextField("Message...", text: $composedMessage).frame(minHeight: CGFloat(36))
                    .padding(.horizontal,12)
                    .overlay(
                        RoundedRectangle(cornerRadius: 15)
                            .stroke(Color.blue, lineWidth: 2)
                    )
                // the button triggers the sendMessage() function written in the end of current View
                Button("Send"){
                    Task {
//                        self.socket.emit("message", roomID, composedMessage)
//                        print(self.messages)
                        self.messages.addItem(item: ChatMessage(message: composedMessage, MatchImg: "yaya", isMe: true))
                        print(self.messages)
                        self.composedMessage = ""
                    }
                }
                Button(action: {}){
                    Image(systemName: "person.fill")
                        .foregroundColor(Color.black)
                    
                }
            }.padding(20)
            // for testing
            //            .task{
            //                print(self.socket.status)
            //                self.socket.emit("identify", userID)
            //
            //                self.socket.emit("subscribe", roomID, otherUser)
            //            }
            
            // that's the height of the HStack
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.