ios - SwiftUI View 不更新

标签 ios swift swiftui swift5 combine

当我更改 ObservableObject 类中数组的属性时,我的 View 没有更新。我什至声明了一个 objectWillChange 属性并手动调用它,但 View 只是随机更新或不是我想要的。我不明白这一点。

import Foundation
import SocketIO
import Combine

class SocketService: ObservableObject {
    static let instance = SocketService()

    let manager = SocketManager(socketURL: URL(RequestURL.base_url)!)
    let socket: SocketIOClient
//    let objectWillChange = ObservableObjectPublisher()

    @Published var allMessages: [Message] = []
//        {
//        willSet {
//            self.objectWillChange.send()
//        }
//    }
    @Published var writtenUsers: [PreviewMessage] = []
//        {
//        willSet {
//            self.objectWillChange.send()
//        }
//    }

    init() {
        socket = manager.defaultSocket
        setSocketEvents()
    }

    // This method is called
    func recieve_read_all_messages() {
        socket.on("recieve-read-all-messages") { (data, ack) in
            guard let arr = data as? [[String: Any]] else { return }
            guard let userID = arr[0]["userID"] as? Int else {
                print("no userID, \(#function), line: \(#line)"); return
            }

            // self.objectWillChange.send()
            for msg in self.allMessages {
                // Here im trying to change the property in the array
                msg.content.readStatus = .read
            }
    }    
}

即使我直接更改此属性, View 也不会更新

@EnvironmentObject private var socketService: SocketService

var body: some View {
    VStack {
         List(filteredMessages, id: \.content.uuid, rowContent: chatSpeechBubbleView)
         sendView
    }
}

private func chatSpeechBubbleView(forMessage message: Message) -> some View {
        ChatSpeechBubble(message: message)
    }

private var sendView: some View {
        Button(action: sendMessage) {
            SFSymbol(.paperplane_fill)
                .fontSize(20)
                .foregroundColor(.white)
                .rotate(.degrees(45))
                .padding(10)
                .padding(.trailing, 5)
                .backgroundColor(.blue)
                .clipCircle()
        }
        .padding(.bottom, 2)
    }

    func sendMessage() {
        for msg in socketService.allMessages {            
            msg.content.readStatus = .read
        }
    }

我应该更新的其他 View :

struct DoubleCheckmark: View {
    var messageContent: MessageContent

    var body: some View {
        HStack(spacing: 0) {
            SFSymbol(.checkmark)
                .resizable()
                .scaledToFit()
            SFSymbol(.checkmark)
                .resizable()
                .scaledToFit()
                .padding(.leading, -9)
        }
        .height(13)
        .foregroundColor(self.messageContent.readStatus == .read ? .blue : .gray)
    }
}
struct ChatSpeechBubble: View {

    // MARK: - init variables
    var message: Message

    // MARK: - normal variables
    var ownSendMessage: Bool {
        message.fromUser.id == UDService.shared.user.id
    }

    // MARK: - Body
    var body: some View {
        messageContent
    }

    private var messageContent: some View {
        HStack(alignment: .bottom) {
            if message.content.text != nil {
                Text(message.content.text!)
                    .foregroundColor(.black)
            }
            if message.content.imageURL != nil {
                Spacer(minLength: 0)
            }
            Text(message.content.timeHourMinute)
                .font(.caption)
                .foregroundColor(.gray)

            if ownSendMessage {
                DoubleCheckmark(messageContent: self.message.content)
            }
        }
    }
}

最佳答案

使用struct , 不是 class

问题是 Message及其子类型应声明为 struct , 而不是 class .原因如下。

这是我用来证明差异的基本示例:

主要代码部分

class SomeObject: ObservableObject {
    @Published var users = [User(username: "George", password: "password")]
}


struct ContentView: View {
    @StateObject var object = SomeObject()

    var body: some View {
        Text("Hello world!")

        let _ = print("Body")
    }
}


let content = ContentView()
print(content.object)
content.object.users[0].password = "password123"

User作为struct

struct User {
    var username: String
    var password: String
}

User作为class

class User {
    var username: String
    var password: String

    init(username: String, password: String) {
        self.username = username
        self.password = password
    }
}
<表类="s-表"> <头> struct class <正文> struct class

查看底部的打印日志。用struct , "Body"又被打印出来了,但是为什么?

这是因为类是通过引用传递的,而结构是通过值传递的。这意味着您可以改变类实例的属性,如下所示:

import SpriteKit

let sprite = SKSpriteNode()
sprite.position = CGPoint(x: 50, y: 50)
sprite.color = .red

如果SKSpriteNode本来是 struct , 与 let属性,您将在尝试更改 let 时收到错误消息常量:

Cannot assign to property: '*' is a 'let' constant

结论

所以因为Messageclass 实例 没有改变。但是有了 struct ,整个事情正在改变。你需要改变整个事情(通过使用 struct )所以 @Published & StateObject/ObservableObject可以观察到变化。

关于ios - SwiftUI View 不更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59464845/

相关文章:

dictionary - 如果我只想确定一个值是否存在,我应该使用哪种 Swift 数据结构?

android - 我应该在 DroneDeploy 应用程序中使用什么样式?

ios - 如何在 Swift 4 的 UIView 中创建带圆角的渐变边框

ios - UICollectionViewDropDelegate 错误的目标索引路径在 `dropSessionDidUpdate`

objective-c - 图片仅在ios6中以纵向模式显示

ios - Realm 最佳实践 : How to handle asynchronous HTTP object update?

ios - 如何监控递归调度队列的任务何时全部完成?

unit-testing - 单元测试 SwiftUI/Combine @Published bool 值

swift - 获取联系人图像 - SwiftUI

ios - 将 tapAction 从 SwiftUI 按钮操作发送到 UIView 函数