Demo 地址: GitHub - DianQK/Use-popToViewController

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

比如:

ENJOY 修改手机号

可能有以下两种方式:

往前两个页就行了,没毛病。

查找一下对应的 ViewController 类型就行了

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

往前两个页的代码大概如下:

1
2
3
4
5
6
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 类型的代码大概如下:

1
2
3
4
5
6
7
// 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 不处理任何转场逻辑。只对外暴露产生的事件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 回哪个页面。