简体   繁体   中英

SwiftUI View doesn't update in a seperate View

I'm using a List to display checkmarks of messages. The issue is if i seperate the checkmarks view into a subview that view doesn't get updated. If i put exactly the same code directly into the List it works as expected. I'm using that checkmarks view in multiple places so it need to be in a subview.

Example:

This doesn't work:

List(filteredMessages, id: \.content.uniqueIdentifier) { message in
     DoubleCheckmark(message: message)
}

But this does work :

List(filteredMessages, id: \.content.uniqueIdentifier) { message in
    HStack(spacing: 0) {
        if message.content.readStatus == .loading {
            Text("loading")
        } else if message.content.readStatus == .sent {
            Image(systemName: "checkmark")
                .resizable()
                .scaledToFit()
                .foregroundColor(message.content.readStatus == .read ? .blue : .gray)
        } else if message.content.readStatus == .received {
            Image(systemName: "checkmark")
                .resizable()
                .scaledToFit()
                .foregroundColor( .gray)

            Image(systemName: "checkmark")
                .resizable()
                .scaledToFit()
                .padding(.leading, -7)
                .foregroundColor(.gray)
        } else if message.content.readStatus == .read {
            Image(systemName: "checkmark")
                .resizable()
                .scaledToFit()
                .foregroundColor(.blue)

            Image(systemName: "checkmark")
                .resizable()
                .scaledToFit()
                .padding(.leading, -7)
                .foregroundColor(.blue)
        } else {

            Text("error")
        }
    }
    .height(11)
    .width(15)
    .yOffset(-1)
}

This is my DoubleCheckmark view:

struct DoubleCheckmark: View {
    var message: Message

    var body: some View {
        HStack(spacing: 0) {
            if message.content.readStatus == .loading {
                Text("loading")
            } else if message.content.readStatus == .sent {
                Image(systemName: "checkmark")
                    .resizable()
                    .scaledToFit()
                    .foregroundColor(message.content.readStatus == .read ? .blue : .gray)
            } else if message.content.readStatus == .received {
                Image(systemName: "checkmark")
                    .resizable()
                    .scaledToFit()
                    .foregroundColor( .gray)

                Image(systemName: "checkmark")
                    .resizable()
                    .scaledToFit()
                    .padding(.leading, -7)
                    .foregroundColor(.gray)
            } else if message.content.readStatus == .read {
                Image(systemName: "checkmark")
                    .resizable()
                    .scaledToFit()
                    .foregroundColor(.blue)

                Image(systemName: "checkmark")
                    .resizable()
                    .scaledToFit()
                    .padding(.leading, -7)
                    .foregroundColor(.blue)
            } else {

                Text("error")
            }
        }
        .height(11)
        .width(15)
        .yOffset(-1)
    }
}

This is my Message class:

public class Message: NSObject, Codable, NSCoding {
    public var content: MessageContent
    public var fromUser: User
    public var toUser: User

    public init(content: MessageContent, fromUser: User, toUser: User) {
        self.content = content
        self.fromUser = fromUser
        self.toUser = toUser
    }

    enum Keys: String {
        case content, fromUser, toUser
    }

    public func encode(with aCoder: NSCoder) {

        aCoder.encode(content, forKey: Keys.content.rawValue)
        aCoder.encode(fromUser, forKey: Keys.fromUser.rawValue)
        aCoder.encode(toUser, forKey: Keys.toUser.rawValue)
    }

    public required convenience init?(coder aDecoder: NSCoder) {

        let content = aDecoder.decodeObject(forKey: Keys.content.rawValue) as! MessageContent
        let fromUser = aDecoder.decodeObject(forKey: Keys.fromUser.rawValue) as! User
        let toUser = aDecoder.decodeObject(forKey: Keys.toUser.rawValue) as! User

        self.init(content: content, fromUser: fromUser, toUser: toUser)
    }

    public override func isEqual(_ object: Any?) -> Bool {
        if let object = object as? Message {
            return self.content.uniqueIdentifier == object.content.uniqueIdentifier
        } else {
            return false
        }
    }
}

If your Message is a class, then List most probably does not update rows for same messages due to equal references of message property. Try to conform your view to Equatable explicitly and override same in Message to make comparison deeply

struct DoubleCheckmark: View, Equatable {
    static func == (lhs: DoubleCheckmark, rhs: DoubleCheckmark) -> Bool {
        lhs.message == rhs.message
    }
    ...

and

class Message: ObservableObject, Equatable {
    static func == (lhs: Message, rhs: Message) -> Bool {
        // compare here all important properties
    }
    ...    

alter this it might be also needed to mentioned explicitly that your custom view is custom equatable

List(filteredMessages, id: \.content.uniqueIdentifier) { message in
     DoubleCheckmark(message: message).equatable() // try with & w/o
}

我将 Message class更改为struct并在CoreData 中使用binary data作为数据类型,而不是需要模型符合NSCodingTransformable

It doesn't update because Message is not ObservableObject. If you pass a class instance you need to add @ObservedObject to the view property. Otherwise, SwiftUI doesn't know that there was a change. Also, the class must either mark the properties that changing with @Published wrapper, or manually call objectWillChange.send()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM