如何链接 ObservableObject?

Posted

技术标签:

【中文标题】如何链接 ObservableObject?【英文标题】:How to chain ObservableObject? 【发布时间】:2020-08-15 12:26:14 【问题描述】:

我有一个可以保存图像的游戏对象。每当找到游戏的图像 URL 时,都应该创建一个新的 GameImage-object 实例。然后它将获取图像并填充 UIImage 属性。发生这种情况时,应更新 UI 以显示图像。

class Game: ObservableObject 
    @Published var image: GameImage?
 

class GameImage: ObservableObject 
    let url: URL
    @Published var image: UIImage?
    
    private var cancellable: AnyCancellable?
    
    init(url: URL) 
        self.url = url
    
    
    func fetch() 
        self.cancellable = URLSession.shared.dataTaskPublisher(for: self.url)
            .map  UIImage(data: $0.data) 
            .replaceError(with: nil)
            .receive(on: DispatchQueue.main)
            .sink(receiveValue:  [weak self] (image) in
                guard let self = self else  return 
                self.image = image
                print(self.url)
                print(self.image)
            )
    
    
    func cancel() 
        cancellable?.cancel()
    
    
    deinit 
        cancel()
    


struct ContentView: View 
    
    @StateObject var game = Game()

    var body: some View 
        VStack 
            if let image = game.image?.image 
                Image(uiImage: image)
             else 
                Text("No image.")
            
        
        .onAppear(perform: 
            guard let gameImageURL = URL(string: "https://cf.geekdo-images.com/itemrep/img/oVEpcbtyWkJjIjk1peTJo6hI1yk=/fit-in/246x300/pic4884996.jpg") else  return 
            game.image = GameImage(url: gameImageURL)
            game.image!.fetch()
        )
    

问题是。获取完成后,调试控制台将显示该图像包含一个 UIImage。但是 UI 不会更新以显示图像。我在这里错过了什么?

【问题讨论】:

你会显示依赖于这个视图模型的 UI 吗? 我不确定你的意思。游戏实例game 应该包含单一的事实来源。游戏对象还可以包含其他属性,例如我从该示例中删除的名称,以免使事情变得过于复杂。每当图像可用时,它就应该呈现它,以这种方式它是依赖的。 【参考方案1】:

有比链接ObservableObject 更简单的解决方案,只需将依赖部分分离到独立的子视图中......所有的都会自动运行。

这是可能的方法。使用 Xcode 12 / ios 14 测试。

struct ContentView: View 

    @StateObject var game = Game()

    var body: some View 
        VStack 
            if nil != game.image 
                GameImageView(vm: game.image!)
            
        
        .onAppear(perform: 
            guard let gameImageURL = URL(string: "https://cf.geekdo-images.com/itemrep/img/oVEpcbtyWkJjIjk1peTJo6hI1yk=/fit-in/246x300/pic4884996.jpg") else  return 
            game.image = GameImage(url: gameImageURL)
            game.image!.fetch()
        )
    


struct GameImageView: View 
    @ObservedObject var vm: GameImage

    var body: some View 
        if let image = vm.image 
            Image(uiImage: image)
         else 
            Text("No image.")
        
    

【讨论】:

不强制解包选项不是更好吗?不能是game.image?.fetch()吗?我知道这是问题所在,但您可以随时改进。 是的,虽然在这个例子中我将 game.image 设置在上面一行,所以它很安全。 我刚刚测试了代码,它工作正常。我仍在尝试围绕这个概念展开思考。 @Mark 当然,但作为一个好习惯,我建议不要强制解包选项。您可以稍后将此行复制到其他地方,然后忘记它。

以上是关于如何链接 ObservableObject?的主要内容,如果未能解决你的问题,请参考以下文章

obs无效的直播地址或直播码

SwiftUI - 如何在@ObservableObject 中获取@EnvironmentObject 数据

如何确认 AppDelegate 的 ObservableObject?

如何使用 ObservableObject 更新 UIViewRepresentable

如何将绑定变量传递给 ObservableObject?

我们如何在 SwiftUI 中读取和写入相同的 ObservableObject?