【发布时间】:2021-05-15 08:12:51
【问题描述】:
我的情况有点复杂。我正在 SwiftUI 中实现一个简单的滚动聊天视图。有一个ChatView 和ChatMessageCells 的列表。每个Message 都有一个User,并且有一个ProfileImage,它是一个由几个字段组成的结构(bucket 和key,用于构造服务器上图像的URL)。这可以在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 {}
【问题讨论】: