在SwiftUI中实现昂贵派生属性的最佳方式



我的情况有些复杂。我正在SwiftUI中实现一个简单的滚动聊天视图。有一个带有ChatMessageCells列表的ChatView。每个Message都有一个User,它有一个由几个字段(bucketkey,用于构造服务器上图像的URL(组成的结构体ProfileImage。这可以周期性地更新User类,也可以是nil

更新:这不是一个特别昂贵的操作,但为了论证起见,让我们说它很昂贵,我只想重新计算一次。基本上有两种方法:当(其中一个(源属性更改时重新计算,或者在需要时重新计算并存储结果。我想知道两者兼顾的最佳方法。

视图需要构造URL,因为它需要为图像指定所需的图像大小。通过这种方式,URL是message.user.profileImage属性的派生属性。

我尝试在ChatMessageCell视图层次结构中使用.onChange(of: self.message.user.profileImage)来计算URL并设置self.profileImageURL,但无法设置简单的属性。因此,我用@State修饰了self.profileImageURL,这允许代码进行编译,并将其分配给init()。但如果是@State,那么这个分配似乎没有任何效果。

所以,我很不确定该怎么做。

ChatViewChatMessageCell看起来像这样:

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的计算属性,则会更简单。您还没有展示MessageUser的实现,所以很难具体说明。如果Message是一个结构,那么您可以将用户作为可观察对象进行引用。

最新更新