有人曾经问我基于Swift的MVVM的实现, 我当时给出的回答就是自己到网上去搜索Swift+MVVM. 我不知道这位哥们是否解决了问题, 但是, 最近我自己在搜索Swift+MVVM的时候, 发现大部分文章都非常有限, 一是不够MVVM, 大部分在玩各种概念, 而事实上, 不管MVC, MVP或者MVVM, 单纯的描述概念没有意义的, 直接一段代码就能非常深刻的理解MVVM的核心逻辑; 二是不够Swift, 要嘛拿KVC来说事, 用Swift实现一套Objective-C的代码, 要嘛直接拿RxSwift来说事, 把原本简单的MVVM逻辑越讲越复杂.
我自己新建了一个项目, 写了一些代码, 试图用最简单的逻辑来解释清楚MVVM是什么.
不管哪种模式, 真正纯正的MVVM是不存在的, 作为开发人员, 真正目的是实现功能的产品化, 而不是完成一个学院派的纯正的模型实现. 我们真正需要搞清的, 就是View和ViewModel之间的关系即可, 至于Model部分的缺失或者iOS开发中天然存在的Controller部分, 就当是一个额外的部分.
另外一点, 我们不要放弃Storyboard, 这是最简单实用的工具, 有好用的工具, 就不要回归到最原始的代码写界面.
我们来假设一个最简单的需求, 就是点击按钮之后, Label和Button的标题都更改掉. 那么, 我们拉好两个控件:
class ViewController: UIViewController {
@IBOutlet private weak var testLabel: UILabel!
@IBOutlet private weak var testButton: UIButton!
...
拉好的控件是没有private的, 加上private是为了防止外部不通过VM强制修改控件.
然后我们再建立一个ViewModel, 对应上这两个控件
class TestViewModel {
private weak var testLabel:UILabel!
private weak var testButton:UIButton!
}
再添加init函数, 将ViewModel的控件和ViewController的控件关联起来.
class TestViewModel {
private weak var testLabel:UILabel!
private weak var testButton:UIButton!
init(l:UILabel, b:UIButton) {
testLabel = l
testButton = b
}
}
其实有更好的办法获取到ViewController中的控件, 并且绑定起来, 不过, 通过init函数是最简单的办法.
之后, 我们在viewDidLoad函数中将ViewModel实例化
class ViewController: UIViewController {
@IBOutlet private weak var testLabel: UILabel!
@IBOutlet private weak var testButton: UIButton!
var vm:TestViewModel!
override func viewDidLoad() {
super.viewDidLoad()
vm = TestViewModel(l: testLabel, b: testButton)
}
}
好了, 这样, 一个简单的ViewModel就对应上实际的View内容了. 之后我们将可以具体数据和控件的变化关联起来. 在这一部分, 大部分网络上文章的做法是使用KVO, 但那其实是一种非常高成本的做法, 代码复杂度平白增加了很多. 我们其实完全可以使用Swift的get/set来实现.
在TestViewModel中添加如下两个函数
var testText:String?{
get { return testLabel.text }
set { testLabel.text = newValue }
}
var submitText:String? {
get { return testButton.title(for: .normal) }
set { testButton.setTitle(newValue, for: .normal) }
}
最后, 我们设定一个点击事件, 通过修改viewModel的实例, 即可快速更改内容
@IBAction func onClick(_ sender: UIButton) {
vm.testText = "aaaa"
vm.submitText = "bbbbb"
}
好了. 这就实现了一个最最基本的MVVM了(事实上, 这是一个VVM), 不过, 这已经够了. 它真正解决的问题, 并不是实现一套多余ViewModel出来, 而是在代码量大到一定程度的时候, 反而会因为ViewModel的存在, 将逻辑变得更加清晰明确, 而这恰恰就是我们的产品意义.
完整的代码如下: