iOS:如何使用 MVVM 将模型从视图模型传递到视图模型?

Posted

技术标签:

【中文标题】iOS:如何使用 MVVM 将模型从视图模型传递到视图模型?【英文标题】:iOS: How to pass a model from view model to view model using MVVM? 【发布时间】:2017-10-04 13:28:28 【问题描述】:

假设我有一个模型 Car,它在 ViewModel1 中实例化,具有以下初始属性:

ViewModel1

let car = Car(make: "McLaren", model: "P1", year: 2015)

然后我需要在下一个视图控制器中完成汽车的附加信息。跟随 MVVM 时,在视图控制器之间传递模型的正确方法是什么?

使用 MVC,操作很简单,因为视图可以引用模型:

vc2.car = car

以下是对该问题的伪尝试,但是我的印象是视图模型应该是私有的,并且只能由单个视图控制器访问。因此,以下尝试对我来说似乎不正确。

ViewController1

fileprivate let viewModel = ViewModel1()

func someMethod() ->   
    let car = self.viewModel.car 
    let vc2 = ViewController2()
    vc2.viewModel.car = car
    present(vc2, animated: true, completion: nil)

ViewController2

let viewModel = ViewModel2()

func anotherMethod() 
    print(self.viewModel.car.make)  //prints "McLaren"

    viewModel.manipulateCar()       //adds additional information to the car object

    print(self.viewModel.car.color) //prints "Black"

    //Pass the finished car to the last view controller to display a summary
    let vc3 = ViewController3()
    vc3.viewModel.car = self.viewModel.car

我想知道我上面展示的是否是使用 MVVM 时做事的好方法,或者如果不是,那么在视图控制器之间传递汽车的最佳方法是什么?

编辑

这个问题与 Class vs Struct 无关。上面的 MVC 示例暗示它将成为一个类(因为它是一个引用)并且它正在多个视图控制器之间传递以完成对象的更多部分。

在跟随MVVM时如何在视图控制器之间传递模型以及视图模型是否应该对视图控制器私有是一个问题。

对于 MVVM,视图控制器不应该引用模型,因此不应该有变量 var car: Car?。因此,您不应该看到:

let vc2 = ViewController2()
vc2.car = car

看到这个会不正确吗?

let vc2 = ViewController2()
vc2.viewModel.car = car

【问题讨论】:

我没有看到任何reactive 的东西,有吗?标签可能令人困惑 【参考方案1】:

这个问题与 RxSwift 无关,甚至与 MVVM 与 MVC 无关。这是一个类与结构的问题。 (请注意,当模型是结构时,您的评论“使用 MVC,这很简单,因为视图可以引用模型”是不正确的,因为您不能将引用传递给结构。)

你如何解决这个问题完全取决于你如何从视图控制器转换到视图控制器。

当视图控制器负责转换时。

当视图控制器负责转换时,每个视图控制器将负责制作下一个视图控制器,每个视图模型将负责制作下一个视图模型。通过让“父”视图模型侦听“子”视图模型(通过委托、回调闭包或反应式可观察)来完成传回模型。

视图控制器可以通过 segue 或“老式方式”直接创建和呈现下一个视图控制器,或者到达它的容器视图控制器(例如导航 VC)并告诉它进行转换来进行转换.

当协调员负责过渡时。

过渡的一个新趋势是让一个协调器类而不是视图控制器来处理它。使用这个想法,协调器持有模型,并根据需要创建视图控制器。然后视图模型与协调器对话,而不是(可能创建和)彼此对话。这样,视图控制器就相互独立了。

您可以使用任何委托、闭包回调或 Rx Observables 让视图模型与协调器对话。


从您的编辑中更新:

您问过let vc2 = ViewController2(); vc2.viewModel.car = car 是否不正确。答案是肯定的,那是不正确的,但很接近。

如果视图控制器负责转换,那么您会看到是这样的:

// in view controller 1
let vc2 = ViewController2()
vc2.viewModel = self.viewModel.viewModel2

如果您使用的是协调器,那么您会看到如下内容:

// in coordinator
let vm2 = ViewModel(car: self.car)
let vc2 = ViewController2(viewModel: vm2)

视图模型背后的关键思想不是它是私有的,它不是必须的。关键思想是它是视图控制器持有的唯一非视图对象。您可以将其视为“模型控制器”。

【讨论】:

我已经更新了我的答案,希望能更好地了解我在寻找什么。不幸的是,这不是一个简单的类与结构问题。我错误地认为它被暗示为一个类。 我已添加更新以响应您的更新。 :-) 每个视图模型将负责制作下一个视图模型。由于它的嵌套 (vc->vm1->vm2) 以及当您真正想要传递的是 model only 时,这似乎不直观。难道就不能在 vc1 中:vc2.viewModel2 = viewModel2(withModel: self.viewModel.car) 吗? @SebastianDwornik 您可以这样做,但是您必须在 viewModel 中公开所有模型对象,其唯一目的是允许 VC1 创建 VM2。视图模型的全部意义在于限制 VC 接触原始模型。或者换一种说法,假设您的 VC 通常是不可测试的,您希望在 VM 中放置尽可能多的逻辑以便可以对其进行测试。通过让 VM1 创建 VM2,您可以进行测试以确保正确创建 VM2。 不同的VC会有不同的VM。这是否意味着 VM 可能需要支持创建各种其他 VM 类型?取决于导航堆栈。【参考方案2】:

长话短说。只需将模型包裹在 child 视图模型中即可。

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) 
    let detailsViewModel = viewModel.detailsViewModel(for: indexPath.row)
    let detailsViewController = DetailsViewController(viewModel: detailsViewModel) 
    present(detailsViewController, animated: true, completion: nil)

detailsViewModelmaster 视图模型中声明如下:

func detailsViewModel(for index: Int) -> DetailsViewModel 
    return DetailsViewModel(car: cars[index])

没有使用协调器。

【讨论】:

以上是关于iOS:如何使用 MVVM 将模型从视图模型传递到视图模型?的主要内容,如果未能解决你的问题,请参考以下文章

如何将条目参数传递给 Xamarin MVVM 视图模型

如何使用 MVVM 灯光从 CustomControl 引用视图模型

将 WPF ValidationRule 的状态传递给 MVVM 中的视图模型

是否可以使用 MVVM 从视图修改视图模型中的属性? [复制]

将 [VisualStateManager] 视图状态绑定到 MVVM 视图模型?

WPF 中的 MVVM - 如何提醒 ViewModel 模型中的变化......或者我应该吗?