如何在 SwiftUI 中通过 ViewModel 传播模型更改?

Posted

技术标签:

【中文标题】如何在 SwiftUI 中通过 ViewModel 传播模型更改?【英文标题】:How to propagate Model changes through ViewModel in SwiftUI? 【发布时间】:2020-09-10 23:52:36 【问题描述】:

我试图了解 SwiftUI 中的 MVVM 模式,但我不完全了解 ViewModel 如何侦听和传播来自 Model 对象的更改。许多示例,包括来自 Apple 的 this one,都谈到了让模型继承自 ObservableObject 并直接在您的视图中使用它。这是有道理的。

但是,ViewModel 应该观察其底层模型变化的最佳/推荐方式是什么?

一个简单的例子是WidgetView,它显示了一个小部件的title,但是title可以在后台网络调用中在后台发生变化。

class WidgetView: View 
    @ObservedObject var widgetVM = WidgetViewModel()

    var body: some View 
        Text(widgetVM.title)
    


class WidgetViewModel : ObservableObject 
   var widget: Widget

   var title: String 
      get 
         // Some translation to the title for this particular view
         return widget.title + "!"
      
   


struct Widget 
    // Some other timer or background process is changing the title
    var title: String

我探索过的一个粗略的解决方案是拥有一个单独的title 并监听变化。因此,如果Widget 扩展了ObservableObject@Published title 字段,那么WidgetViewModel 可以执行以下操作:

class WidgetViewModel : ObservableObject 
   var widget: Widget

   @Published var title: String = ""
   var cancellable: AnyCancellable?

   init() 
       self.cancellable = widget.$title.receive(on: DispatchQueue.main)
           .sink(receiveValue: self.updateTitle )
   

   func updateTitle(_: String) 
       self.title = widget.title + "!"
   

Widget 是否也推荐/标准扩展 ObservableObject?如果是这样,WidgetViewModel 如何正确传递更改通知?看起来WidgetModelView.widget 需要同时是@Published 和@ObservedObject,但这似乎不对。

有人对此有任何见解吗?

【问题讨论】:

你不需要widget@Published。您的Widget 可以通过组合发布对title 的更改,如您所展示的,或者您可以使用Notification 或您喜欢的任何其他方法。您不需要Widget 符合ObservableObject 即可拥有title 的发布者。您可以为title 公开Subject 假设我使用了从模型到视图模型的不同通知机制(我将研究这些),有没有办法在没有我设置的 Published 属性的情况下通知视图(即我不'不需要在 ViewModel 中有一个单独的标题字段)? 不,这是同时拥有模型和视图模型的成本。你的视图模型通常有一堆胶水代码来向上/向下传播事件。 【参考方案1】:

你的Widget是struct,所以它不能是ObservableObject,你可以这样做

class WidgetViewModel : ObservableObject 

   var widget: Widget 
     didSet 
        updateTitle(widget)    // << here !!
     
   

   @Published var title: String = ""

   func updateTitle(_: String) 
       self.title = widget.title + "!"
   

【讨论】:

如果您更新 widget.title,这将无济于事。来自网络请求(我相信这就是问题所在)。

以上是关于如何在 SwiftUI 中通过 ViewModel 传播模型更改?的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI 将 Int 值传递给 ViewModel

在 SwiftUI 中通过摇动手势(使用 UIKit)设置 EnvironmentObject 不会更新视图

在 SwiftUI 中通过 Date() 读取星期几以获取本地通知

使用 SwiftUI/Combine,如何避免在 ViewModel 中放置可取消项

SwiftUI:如何从 ViewModel 的更改中切换警报演示者

SwiftUI 如何不在列表中复制 ViewModel