之前我一直在 ViewModel 中处理页面跳转逻辑,VC 直接拿到目标页面进行 push/present。前些天 Code Review 时,同事指出理论上 ViewModel 里不应该出现跟 UIKit 相关的东西。
我觉得很有道理,但我也不赞成把页面跳转逻辑写到 VC 里,我认为在 MVVM 中 VC 中充当了一个 ContainerView 的角色,管理 View 的生命周期,做一些依赖注入之类的事情辅助 View 和 ViewModel 的绑定,它是 View 层的。页面跳转中只有 push/present 这一步是 View 层的,其他诸如通过一些逻辑判断该跳转到哪个页面、跳转之前需要做什么操作之类的代码不该放在 View 层。
但 ViewModel 中出现 UIKit 对象这一点也确实让人难受,于是我用我蹩脚的英语询问了 Rx 社区的外国友人,但结果并不尽如人意:
RxCommunity1.png
RxCommunity2.png
他们向我推荐了一篇文章和一个库,虽然并不是我想要的,但还是很感谢他们的热心帮助。第二天我自己写了一个 Router:
typealias Completion = () -> Void
enum PageMap {
case DemoPage1
case DemoPage2(String)
var vc: UIViewController {
switch self {
case .DemoPage:
return DemoController1()
case .DemoPage2(let title):
return DemoController2(title: title)
}
}
}
enum Router {
case Push(PageMap)
case Present(PageMap)
func go(form vc: UIViewController,
animated: Bool = true,
completion: Completion? = nil) {
switch self {
case .Push(let map):
vc.navigationController?.pushViewController(map.vc, animated: animated)
case .Present(let map):
vc.presentViewController(map.vc, animated: animated, completion: completion)
}
}
}
有了这个 Router,ViewModel 就可以提供 Driver<Router>,只要在 VC 里订阅它就好。
果然没有什么问题是加一层抽象不能解决的,科科。









网友评论
(https://medium.com/@red010182/thanks-for-sharing-i-want-to-know-more-about-user-interactions-in-mvvm-pattern-ac9175297600#.e1159k5p3)
經過簡單的討論後,目前就我的認知是,MVVM的best practice是ViewModel不處裡UI問題,它反而是View跟VC的橋樑,在View跟VC之間反覆傳遞UI responses。我認為這其實是一件非常困擾的事情,需要寫很多“橋樑代碼”。
舉個例子好了,如果你有一個UIButton,點擊之後需要彈出UIAlert,使用者點擊不同選項,再回到VM做相對應的處理。這麼一個簡單的場景,VM需要兩次proxy,一次從UIButton到VC,一次從VC回到VM。我有時候在想,這樣真的有比較好嗎?如果有大量的UI元件,大量的使用者操作,對應到許許多多不同的邏輯處理,這豈不是累死人?
所以我一直遲遲無法完全切換至MVVM。我一直想知道,真正使用MVVM的人,究竟是如何處理這樣的UI的各種反饋?
謝謝