Reputation: 8687
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.
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
}
}
}
Upvotes: 1
Views: 291
Reputation: 1
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()
Upvotes: 0
Reputation: 8687
I changed my Message class
to a struct
and used binary data
for the datatype in CoreData instead of Transformable which needs the model to conform to NSCoding
Upvotes: 0
Reputation: 257493
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
}
Upvotes: 1