正确使用 popViewController

Demo 地址: Use-popToViewController (opens new window)

在 Push 了多个页面后,有时我们需要回到之前的某一页,这就比较麻烦了,我们需要确定返回的 ViewController 的位置。

比如:

Danger

可能有以下两种方式:

  • 直接退回两个页,没毛病
  • 查找一下对应的 ViewController 类型

以上两种 可能 写起来快捷一些,但这之中存在很多潜在问题。

退回两个页的代码大概如下:

saveBarButtonItem.rx.tap
    .subscribe(onNext: { [unowned self] in
        let destinationViewController = self.navigationController!.viewControllers[self.navigationController!.viewControllers.index(self.navigationController!.viewControllers.endIndex, offsetBy: -3)]
        self.navigationController?.popToViewController(destinationViewController, animated: true)
    })
    .disposed(by: disposeBag)

直接读这段代码我们并不能理解我到底要回到的是具体哪个页面,当业务有调整时,比如中间又加了一页,== 这就很尴尬了,还要额外记得修改这里的 offset 。 或者是当这个设置手机号页面有多种转换场景,比如回到前两页,回到上一页,甚至是进入到一个新的页面。这些都用 index 写到一个页面就显得更加难理解了。 查找一下对应的 ViewController 类型的代码大概如下:

// SetNewMobileNumberViewController.swift
saveBarButtonItem.rx.tap
    .subscribe(onNext: { [unowned self] in
        let destinationViewController = self.navigationController!.viewControllers.first(where: { $0 is RootViewController })!
        self.navigationController?.popToViewController(destinationViewController, animated: true)
    })
    .disposed(by: disposeBag)

判断一下对应的 ViewController 是不是 RootViewController 。 这样一来。 SetNewMobileNumberViewController 依赖了 RootViewController ,这是不友好的。

我们可能觉得应当用 Router 去做这件事,我觉得也不怎么合适,我们仍然需要在 SetNewMobileNumberViewController 写各种各样的返回场景,本质上还是依赖了 RootViewController (只不过我们换成了对应的 URL )。 将转场逻辑交给 RootViewController 处理 ,这可能是比以上两种方法更优雅的方案了。 VerifyCurrentMobileNumberViewControllerSetNewMobileNumberViewController 不处理任何转场逻辑。只对外暴露产生的事件。

modifyPasswordButton.rx.tap
    .flatMap { [unowned self] () -> Observable<()> in
        let verifyCurrentMobileNumberViewController = VerifyCurrentMobileNumberViewController()
        self.show(verifyCurrentMobileNumberViewController, sender: nil)
        return verifyCurrentMobileNumberViewController.nextTap
    }
    .flatMap { [unowned self] () -> Observable<()> in
        let setNewMobileNumberViewController = SetNewMobileNumberViewController()
        self.show(setNewMobileNumberViewController, sender: nil)
        return setNewMobileNumberViewController.saveTap
    }
    .subscribe(onNext: { [unowned self] in
        self.navigationController?.popToViewController(self, animated: true)
    })
    .disposed(by: disposeBag)

self.navigationController?.popToViewController(self, animated: true) 这就清晰准确的表达了我们想要 pop 回哪个页面。