为每个UITableViewCell创建视图模型 [英] Creating view-model for each UITableViewCell

查看:137
本文介绍了为每个UITableViewCell创建视图模型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我坚持为表视图单元格创建视图模型的设计决策。每个单元格的数据由数据源类(具有 Contacts 的数组)提供。在 MVVM 中,只有视图模型可以与模型对话,但是将数据源置于视图模型中没有意义,因为它可以访问所有单元格的数据,将数据源放在视图控制器中也是错误的,因为它不能引用数据。还有一些其他关键时刻:

I'm stuck on a design decision with creating view-models for table view's cells. Data for each cell is provided by a data source class (has an array of Contacts). In MVVM only view-model can talk to model, but it doesn't make sense to put data source in view-model because it would make possible to access data for all cells, also it's wrong to put data source in view controller as it must not have reference to the data. There are some other key moments:


  • 每个单元格必须具有其自己的视图模型实例,而不是共享的实例

  • cellForRowAtindexPath 不能放置在视图模型中,因为
    不应包含任何UI引用

  • View / ViewController的视图模型不应与单元格的视图模型交互

  • Each cell must have it's own instance of view-model not a shared one
  • cellForRowAtindexPath must not be placed in a view-model because it shouldn't contain any UI references
  • View/ViewController's view-model should not interact with cell's view-model

插入数据源的正确方法是什么 MVVM 关系中的单元格?谢谢。

What's the right way to "insert" data source for cells in MVVM's relationship ? Thanks.

推荐答案

让我从一些理论入手。 MVVM是针对Microsoft的Silverlight和WPF的演示模型(或应用程序模型)的专业化版本。
这种UI体系结构模式的主要思想是:

Let me start with some theory. MVVM is a specialization of the Presentation Model (or Application Model) for Microsoft's Silverlight and WPF. The main ideas behind this UI architectural pattern are:


  • 视图部分是唯一依赖于GUI框架的部分。这意味着对于iOS,视图控制器是视图的一部分。

  • 视图只能与视图模型对话。 从不访问该模型。

  • 视图模型保存视图的状态。该状态通过视图模型属性提供给视图。这些属性不仅包含标签的值,还包含其他与视图相关的信息,例如启用了保存按钮或评级视图的颜色。但是状态信息必须独立于UI框架。因此,在iOS中,颜色的属性应为枚举,例如,而不是UIColor。

  • 视图模型还提供了处理UI操作的方法。 。该动作将与模型对话,但是它们永远不会更改与数据直接相关的视图的状态。取而代之的是,它与模型对话并要求进行必要的更改。

  • 模型应为自治,即,您应该能够对模型使用相同的代码命令行应用程序和UI界面的模型。它将处理所有业务逻辑。

  • 该模型不了解视图模型。因此,对视图模型的更改将通过观察机制传播。对于iOS和带有普通NSObject子类甚至核心数据的模型,可以使用KVO(对于Swift也是如此)。

  • 一旦视图模型知道模型中的更改,它应该更新其持有的状态(如果使用值类型,则应创建一个已更新的状态并替换它)。

  • 视图模型不了解该视图。在其最初的概念中,它使用数据绑定,而iOS则不可用。因此,视图模型中的更改通过观察机制传播。您也可以在这里使用KVO,或者如您在问题中提到的,使用简单的委托模式(如果与Swift属性观察者结合使用,则更好)。有些人更喜欢反应性框架,例如RxSwift,ReactiveCocoa甚至是Swift Bond。

  • The view part is the only one that depends on the GUI framework. This means that for iOS, the view controller is part of the view.
  • The view can only talk to the view model. Never to the model.
  • The view model holds the state of the view. This state is offered to the view via view model properties. These properties contain not only the value of the labels, but also other view related information like if the save button is enabled or the color for a rating view. But the information of the state must be UI framework independent. So in the case of iOS, the property for the color should be an enum, for example, instead of a UIColor.
  • The view model also provides methods that will take care of the UI actions. This actions will talk to the model, but they never change the state of the view that is data related directly. Instead, it talks to the model and asks for the required changes.
  • The model should be autonomous, i.e. you should be able to use the same code for the model for a command line application and a UI interface. It will take care of all the business logic.
  • The model doesn't know about the view model. So changes to the view model are propagated through an observation mechanism. For iOS and a model with plain NSObject subclasses or even Core Data, KVO can be used for that (also for Swift).
  • Once the view model knows about changes in the model, it should update the state that it holds (if you use value types, then it should create an updated one and replace it).
  • The view model doesn't know about the view. In its original conception it uses data binding, that not available for iOS. So changes in the view model are propagated through an observation mechanism. You can also use KVO here, or as you mention in the question, a simple delegation pattern, even better if combined with Swift property observers, will do. Some people prefer reactive frameworks, like RxSwift, ReactiveCocoa, or even Swift Bond.

好处是您提到的:


  • 更好的关注点分离。

  • UI独立性:易于迁移到其他UI。

  • 由于关注点的分离和代码的分离特性,使得可测性更好。

所以回到您的问题, UITableViewDataSource 协议的实现属于体系结构的视图部分,因为它依赖于UI框架。请注意,为了在代码中使用该协议,该文件必须导入UIKit。同样,返回视图的 tableView(:cellForRowAt:)之类的方法也很大程度上取决于UIKit。

So coming back to your question, the implementation of the UITableViewDataSource protocol belongs to the view part of the architecture, because of its dependencies on the UI framework. Notice that in order to use that protocol in your code, that file must import UIKit. Also methods like tableView(:cellForRowAt:) that returns a view is heavily dependent on UIKit.

然后,您的 Contacts 的数组,实际上就是您的模型,无法通过视图(数据源或其他)进行操作或查询。相反,您将视图模型传递给表视图控制器,在最简单的情况下,该模型具有两个属性(我建议存储它们,而不是计算属性)。其中一个是节数,另一个是每个节的行数:

Then, your array of Contacts, that is indeed your model, cannot be operated or queried through the view (data source or otherwise). Instead you pass a view model to your table view controller, that, in the simplest case, has two properties (I recommend that they are stored, not computed properties). One of them is the number of sections and the other one is the number of rows per section:

var numberOfSections: Int = 0
var rowsPerSection: [Int] = []

使用对模型的引用初始化视图模型,并在初始化的最后一步设置这两个属性的值。

The view model is initialized with a reference to the model and as the last step in the initialization it sets the value of those two properties.

视图控制器中的数据源使用视图模型的数据:

The data source in the view controller uses the data of the view model:

override func numberOfSections(in tableView: UITableView) -> Int {
    return viewModel.numberOfSections
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return viewModel.rowsPerSection[section]
}

最后可以每个单元格都有不同的视图模型结构:

Finally you can have a different view model struct for each of the cells:

struct ContactCellViewModel {
    let name: String

    init(contact: Contact) {
        name = contact.name ?? ""
    }
}

UITableViewCell 子类将知道如何使用该结构:

And the UITableViewCell subclass will know how to use that struct:

class ContactTableViewCell: UITableViewCell {
    
    var viewModel: ContactCellViewModel!

    func configure() {
        textLabel!.text = viewModel.name
    }
}

为了使每个单元都有对应的视图模型,表视图视图模型将提供一种生成它们的方法,该方法可用于填充视图模型的数组:

In order to have the corresponding view model for each of the cells, the table view view model will provide a method that generates them, and that can be used to populate the array of view models:

func viewModelForCell(at index: Int) -> ContactCellViewModel {
    return ContactCellViewModel(contact: contacts[index])
}

如您所见,这里的视图模型是唯一与模型对话的人(您的 Contacts 数组),而视图仅与视图模型对话。

As you can see the view models here are the only ones talking to the model (your Contacts array), and the views only talk to the view models.

希望这会有所帮助。

这篇关于为每个UITableViewCell创建视图模型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆