【问题标题】:Handle events in subviews in MVVM in Swift在 Swift 中处理 MVVM 中的子视图中的事件
【发布时间】:2020-02-21 20:10:08
【问题描述】:

我正在尝试在 Swift 中进入 MVVM,我想知道如何处理 MVVM 中子视图中的事件,以及这些事件如何沿视图/视图模型链向上传播。我现在说的是纯 Swift(没有 SwiftRx 等)。

示例

假设我有一个TableViewController 和一个TableViewModel。视图模型包含一个对象数组并为每个对象创建一个TableCellViewModel,因为每个单元格代表这些对象之一。 TableViewController 从其模型和每个单元格的视图模型中获取要显示的行数,因此它可以将其传递给单元格。

然后我们有一个TableCell,每个单元格都有一个TableCellViewModelTableCell 查询其模型以获取面向用户的字符串等内容。

现在假设TableCell 也有一个删除按钮,用于删除该行。我想知道如何处理:通常,单元格会将按钮按下转发到其视图模型,但这不是我们需要它的地方 - 我们最终需要了解TableViewControllerTableViewModel 中的按钮按下,因此我们可以从表格视图中删除该行。

所以问题是:
按钮事件如何从 MVVM 中的视图链中的 TableCell 向上获取?

代码

根据 cmets 的要求,示例中的代码:

class TableViewController: UIViewController, UITableViewDataSource {

    var viewModel: TableViewModel = TableViewModel()

    // setup and such

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.viewModel.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableCell
        cell.viewModel = self.viewModel.cellViewModel(at: indexPath.item)
        return cell
    }

}

class TableViewModel {

    // setup, get data from somewhere, ...

    var count: Int {
        return self.modelObjects.count
    }

    func cellViewModel(at index: Int) -> TableCellViewModel {
        let modelObject = self.modelObjects[index]
        let cellViewModel = TableCellViewModel(modelObject: modelObject)
        return cellViewModel
    }

}

class TableCell {

    var viewModel: TableCellViewModel!

    // setup UI, do what a cell does

    func viewModelChanged() {
        self.titleLabel.text = self.viewModel.title()
    }

    func deleteButtonPressed(_ sender: UIButton) {
        // Oh, what to do, what to do?
    }

}

class TableCellViewModel {

    private var modelObject: ModelObject

    init(modelObject: ModelObject) {
        self.modelObject = modelObject
    }

    func title() -> String {
        return self.modelObject.title
    }

}

【问题讨论】:

  • 我每天都会遇到的有趣问题。我有质量差的解决方案,如果你愿意,稍后再写。
  • 你能展示你的tableview数据源方法吗?
  • 这只是一个超级简单的例子,但我添加了一些代码来更好地说明这个例子
  • @BlackWolf 我使用cellViewModel 作为数据存储和预处理器。所有用户操作都使用委托模式发送到viewController

标签: ios swift user-interface mvvm


【解决方案1】:

也许你可以使用nextResponder util nextResponder 是 VC,而 VC 响应者来委托(例如:CellEventDelegate)来处理删除数据和单元格

UIResponder *nextResponder = pressedCell.nextResponder;
        while (nextResponder) {
            if ([nextResponder conformsToProtocol:@protocol(CellEventDelegate)]) {
                if ([nextResponder respondsToSelector:@selector(onCatchEvent:)]) {
                    [((id<CellEventDelegate>)nextResponder) onCatchEvent:event];
                }
                break;
            }
            nextResponder = nextResponder.nextResponder;
        }

【讨论】:

  • 我已经考虑过了,我认为从技术上讲你可以让它工作,但我更感兴趣的是如何在 MVVM 模式中处理这种事情
【解决方案2】:

TableViewModel 是事实的来源,所以所有的全局操作都应该在那里执行。按下按钮完全是 UI 操作,viewModel 不应该直接处理。

所以,现在我们知道两个事实:

  1. TableViewModel 应该从数组中删除单元格,然后 viewController 应该处理删除动画过程;
  2. 不应在子 viewModel 中处理按钮按下。

据此,您可以通过以下方式实现:

  1. 将按钮按下事件传递给 viewController(使用回调或委托模式);
  2. 调用TableViewModel方法删除特定单元格:

    viewModel.deleteCell(at: indexPath)

  3. 正确处理 viewController 中的删除动画。

【讨论】:

  • 我已经阅读了更多关于 MVVM 的内容,我认为这是“正确”的解决方案。我认为视图应该将事件传递给视图模型,但它们应该将事件传递给控制器​​。我仍然会感兴趣:应该保留单元格的视图模型,还是应该将所有内容放在一个模型中?
  • @BlackWolf 我的直觉是您可以丢弃TableCellViewModel,并在构建每个单元格数据时简单地填充它,因为此时您可以访问该信息(TableViewModel)。但是,我可能错了-如果我自己的知识没有空白,我就不会回答这个问题。大声笑
  • 我自己的问题:@pacification,如果我有 UITableView 的通用子类,那我将如何处理?在这个例子中,viewModel 方便地存在于 tableView 中,对它的任何调用都非常简单。但是,如果使用通用 UITableView 子类 - 是否会在其中存储对 viewModel 的引用?希望我说得通……谢谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-27
  • 2020-03-12
  • 1970-01-01
相关资源
最近更新 更多