【问题标题】:How to pass Model data from ViewController to ViewModel如何将模型数据从 ViewController 传递到 ViewModel
【发布时间】:2018-12-25 05:47:57
【问题描述】:

我正在修改我的应用以实现MVVM 模式。

简介:

我使用WebServices 来获取我的所有数据。

在应用程序启动时,在加载屏幕期间,我向所有 Web 服务发出请求,以便我的所有数据都可用。

例如,当我将点击 App 中的菜单以访问特定场景时,我已经拥有所需的所有数据,并且我将模型传递给相应的 ViewController 并使用 Segue

问题:

真正的问题是,将这个模型数据传递给我的ViewModel 的好方法是什么?

我正在使用实现Codable 协议的结构在请求Web 服务并解析json 数据后获取我的模型。

这是我的 ViewController 的样子:

class VigilanceViewController: UIViewController {

    @IBOutlet var outletTableView: UITableView!

    var vigilance: Vigilance?

    fileprivate let viewModel = VigilanceViewModel()

    override func viewDidLoad() {

        super.viewDidLoad()

        if let vigilance = vigilance {
            // pass data to the viewModel
        }

        outletTableView.dataSource = viewModel
    }
}

在ViewModel中,不知道如何声明Vigilance模型,也不知道如何正确初始化ViewModel。

这是视图模型:

class VigilanceViewModel: NSObject {

    var items = [VigilanceViewModelItem]()
    var maxVigilance: Int = 0
    var vigilance: Vigilance? = nil

    override init() {
        super.init()

        // Append objects in my items array from the vigilance Model so that the dataSource is ready to go and clean

    }
}

extension VigilanceViewModel: UITableViewDataSource {

    func numberOfSections(in tableView: UITableView) -> Int {
        return maxVigilance - 1
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return items[section].rowCount
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell = UITableViewCell()

        // TODO

        return cell
    }
}

因为我的模型包含很多我目前在这个 ViewController 中不需要的信息,所以我正在制作一个 items 数组,以便在我的 DataSource 中更容易使用。

这是我的警戒模型的样子:

struct Vigilance: Codable {

    let result: Result

    struct Result: Codable {
        let startDate: Int
        let endDate: Int
        let sendDate: Int
        let advice: String?
        let comment: String?
        let colorMax: Int
        let details: [VigilanceDetails]?

        struct VigilanceDetails: Codable {
            let dptNumber: String
            let dptName: String
            let color: String
            let risks: [Risks]?

            struct Risks: Codable {
                let code: String
                let label: String
            }
        }
    }
}

我第一个猜测为什么我不能在没有可选的情况下声明我的模型是因为我没有自定义初始化器吗?

但这不是这里的主要问题。

我想知道如何将这个模型数据(之前在这个VC中从prepareForSegue获得)传递给ViewModel,并正确初始化ViewModel。

如果有人可以在这里指导我,将不胜感激。

我正在探索操作系统和设计模式中一些晦涩难懂的部分,尽我所能理解和应用我所学的内容。

【问题讨论】:

  • 您可以将 InitialViewController 配置为加载屏幕并在 AppDelegate(“AppController”) 中加载您的数据,然后使用其自定义初始化程序实例化您的 contentViewController 并将其分配给 window.contentViewController = myContentViewController 以替换加载屏幕.
  • 你必须在开始时显示一些东西,并且 ViewController 将由应用程序初始化,因此将使用 required init(decoder: Decoder) 从情节提要加载其设置,因此你将无法实例化它带有初始化程序。此外,您可以放入 xib 并使用自定义初始化进行实例化(或在情节提要中并使用设置了 ViewController 设置的静态生成函数进行实例化)。
  • 另一个提示:也许可以调用“ViewModel”之类的东西,比如模型、模型控制器、控制器来区分它的目的是什么(修改或成为模型)。 Views 可以是 ViewController 和 Views,Controllers 可以是 Controllers 或 ViewControllers。也许这种区别只是我所做的。
  • 你也可以用枚举来建模你的状态,它在开头 .empty ,然后你用viewModel.state = .data(vigilance: Vigilance)分配你的数据。
  • 你熟悉 MVVM 架构吗?

标签: ios swift mvvm viewmodel codable


【解决方案1】:

同样的声明但是在 ViewModel 中呢?

class VigilanceViewModel: NSObject {
    let items: [VigilanceViewModelItem]
    let maxVigilance: Int
    let vigilance: Vigilance

    init(items: [VigilanceViewModelItem], vigilance: Vigilance) {
        self.items = items
        self.vigilance = vigilance
        maxVigilance = vigilance.max
    }
}

【讨论】:

  • 我试过了,但是后来在ViewController中,怎么在initializer方法中传递警戒模型呢? fileprivate 让 viewModel = VigilanceViewModel()
  • 我刚刚在另一个答案中写道,如何使用参数初始化视图控制器。把param: String改成viewModel: VigilanceViewModel你就会找到答案
【解决方案2】:

关于为什么我不能在没有可选的情况下声明我的模型的第一个猜测是 因为我没有自定义初始化程序吗?

只要你用 segues 初始化你的 UIViewControllers,你就不能使用一个自定义的初始化器来传递你想要的对象。 如果你想避免可选变量,我建议编写一个 init 函数,类似这样(并将你的 VC 移动到 xib 文件):

class ViewController: UIViewController {
    let param: String

    init(param: String) {
        self.param = param
        super.init(nibName: "YourViewControllerNibsName", bundle: Bundle.main)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

真正的问题是,将这个模型数据传递给我的 ViewModel 的好方法是什么?

一旦你从代码而不是 segues 启动你的视图控制器,你可以编写一些辅助函数,通常我把它放在一个路由器(或你喜欢的名字)类中,比如:

class AppRouter {
    // Return UIViewController not to expose the type of ViewController
    static func getViewController(with param: String) -> UIViewController {
        return ViewController(param: param)
    }
}

// Using it from an another VC
let actualVC: UIViewController!
let vcToPresent = AppRouter.getViewController(with: "yourParam")
actualVC.present(vcToPresent, animated: true, completion: nil)

【讨论】:

  • 同样的声明但是在 ViewModel 中呢? (实际上这个更重要,因为 ViewController 中的可选,它只是一个 if let 检查,不是那么烦人,但仍然,谢谢你的提示)。
  • 更新了答案
猜你喜欢
  • 2017-02-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多