我的情况有些复杂。我正在SwiftUI中实现一个简单的滚动聊天视图。有一个带有ChatMessageCell
s列表的ChatView
。每个Message
都有一个User
,它有一个由几个字段(bucket
和key
,用于构造服务器上图像的URL(组成的结构体ProfileImage
。这可以周期性地更新User
类,也可以是nil
。
更新:这不是一个特别昂贵的操作,但为了论证起见,让我们说它很昂贵,我只想重新计算一次。基本上有两种方法:当(其中一个(源属性更改时重新计算,或者在需要时重新计算并存储结果。我想知道两者兼顾的最佳方法。
视图需要构造URL,因为它需要为图像指定所需的图像大小。通过这种方式,URL是message.user.profileImage
属性的派生属性。
我尝试在ChatMessageCell
视图层次结构中使用.onChange(of: self.message.user.profileImage)
来计算URL并设置self.profileImageURL
,但无法设置简单的属性。因此,我用@State
修饰了self.profileImageURL
,这允许代码进行编译,并将其分配给init()
。但如果是@State
,那么这个分配似乎没有任何效果。
所以,我很不确定该怎么做。
ChatView
和ChatMessageCell
看起来像这样:
struct ChatView : View
{
@ObservedObject public var stream : ChatStream
var body: some View {
ScrollViewReader { scrollView in
ScrollView {
LazyVStack {
ForEach(self.stream.messages) { inMsg in
ChatMessageCell(message: inMsg)
}
}
}
}
}
struct
ChatMessageCell: View
{
public let message : ChatStream.Message
@State var profileImageURL : URL?
init(message inMessage: ChatStream.Message)
{
self.message = inMessage
let image = inMessage.user.profileImage
let url = image?.sharpImageURL(forSize: kProfileImageSize)
self.profileImageURL = url // <-- doesn't assign if @State
}
var body: some View
{
VStack(alignment: .leading)
{
Text("Key: (self.message.user.profileImage?.key ?? "nil")")
Text("URL: (self.profileImageURL?.absoluteString ?? "nil")")
// This is just for debugging. Really there's a `KFImage` here that’s
// supposed to async load the image.
}
.onChange(of: self.message.user.profileImage)
{ inVal in
let url = inVal?.sharpImageURL(forSize: kProfileImageSize)
self.profileImageURL = url // <-- can't modify if not @State
}
}
}
其他类别:
class ChatStream
{
public struct Message
{
var id : Int
var date : Date
var message : String
var user : User
init(fromIncoming inMsg: IncomingMessage, user inUser: User)
{
self.id = inMsg.id
self.date = inMsg.date
self.message = inMsg.message
self.user = inUser
}
}
public class User
{
typealias ID = String
let id : ID
var username : String
var onlineAt : Date?
@Published var profileImage : ProfileImage?
init(fromIncoming inUser: IncomingUser)
{
self.id = inUser.id
self.username = inUser.username
self.onlineAt = inUser.onlineAt
self.profileImage = inUser.profileImage
}
func update(fromIncoming inUser: IncomingUser)
{
self.username = inUser.username
self.onlineAt = inUser.onlineAt
self.profileImage = inUser.profileImage
}
}
@Published var messages = OrderedSet<Message>()
@Published var users = [User.ID : User]()
}
extension ChatStream : ObservableObject {}
extension ChatStream.ProfileImage : Equatable {}
如果message
是一个可观察的对象,当用户属性更新时会触发更改,并且URL是message
的计算属性,则会更简单。您还没有展示Message
或User
的实现,所以很难具体说明。如果Message
是一个结构,那么您可以将用户作为可观察对象进行引用。