使用 MVVM 架构时页面跳转逻辑写在哪儿?

3,535 阅读1分钟

之前我一直在 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,只要在 VC 里订阅它就好。

果然没有什么问题是加一层抽象不能解决的,科科。