之前我一直在 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 里订阅它就好。
果然没有什么问题是加一层抽象不能解决的,科科。