如何从PersonalInfoViewController返回至非窗口根的RootViewController
从嵌套导航的PersonalInfoViewController返回RootViewController的解决方案
我来给你梳理几种靠谱的实现方式,针对你这种「外层导航控制器包含RootVC,RootVC内部又嵌套了一个导航控制器,最后要从内层导航的PersonalInfoVC跳回RootVC」的场景,而且RootVC还不是window的根控制器:
方法一:闭包回调(最推荐,低耦合)
这种方式是在创建内层导航控制器时,给PersonalInfoVC传递一个回调闭包,点击返回时触发闭包让RootVC自己处理返回逻辑,耦合度最低,也最容易维护。
步骤1:在PersonalInfoViewController里定义闭包
class PersonalInfoViewController: UIViewController { // 定义返回回调闭包 var backToRootHandler: (() -> Void)? // 返回按钮点击事件 @IBAction func backToRootButtonTapped(_ sender: UIButton) { // 触发回调 backToRootHandler?() // 可选:先把内层导航栈清空(视觉上更流畅) navigationController?.popToRootViewController(animated: true) } }
步骤2:在RootViewController里传递闭包
当你给RootVC的容器添加内层导航控制器时,给PersonalInfoVC绑定回调:
class RootViewController: UIViewController { private var innerNavigationController: UINavigationController? override func viewDidLoad() { super.viewDidLoad() setupInnerNavigation() } private func setupInnerNavigation() { // 创建内层导航的初始VC(比如你的第一个VC) let firstVC = FirstViewController() let innerNav = UINavigationController(rootViewController: firstVC) // 假设你已经把后续的VC都push到innerNav里,最后一个是PersonalInfoVC if let personalInfoVC = innerNav.viewControllers.last as? PersonalInfoViewController { // 绑定回调,用weak self避免循环引用 personalInfoVC.backToRootHandler = { [weak self] in guard let self = self else { return } // 让外层导航控制器pop到当前RootVC self.navigationController?.popToViewController(self, animated: true) // 可选:如果不需要保留内层导航,可以移除它 self.innerNavigationController?.removeFromParent() self.innerNavigationController?.view.removeFromSuperview() } } // 把内层导航添加到容器视图 innerNavigationController = innerNav addChild(innerNav) innerNav.view.frame = containerView.bounds containerView.addSubview(innerNav.view) innerNav.didMove(toParent: self) } }
方法二:通知中心(适合跨多层级通信)
如果你的视图层级比较复杂,闭包传递不太方便,可以用通知中心来触发返回逻辑,但要注意及时移除观察者避免内存泄漏。
步骤1:在RootViewController里监听通知
class RootViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // 监听返回通知 NotificationCenter.default.addObserver( self, selector: #selector(handleBackToRoot), name: NSNotification.Name("BackToRootVCNotification"), object: nil ) } @objc private func handleBackToRoot() { // 执行返回操作 navigationController?.popToViewController(self, animated: true) // 可选:清理内层导航 innerNavigationController?.removeFromParent() innerNavigationController?.view.removeFromSuperview() } deinit { // 一定要移除观察者 NotificationCenter.default.removeObserver(self) } }
步骤2:在PersonalInfoViewController里发送通知
class PersonalInfoViewController: UIViewController { @IBAction func backToRootButtonTapped(_ sender: UIButton) { // 发送通知 NotificationCenter.default.post( name: NSNotification.Name("BackToRootVCNotification"), object: nil ) // 清空内层导航栈 navigationController?.popToRootViewController(animated: true) } }
方法三:遍历视图层级(直接但维护性差)
你也可以通过递归遍历视图控制器的层级,直接找到RootVC,然后让外层导航控制器pop到它。这种方式比较直接,但如果后续视图结构变化,代码可能会失效,维护性稍差。
class PersonalInfoViewController: UIViewController { @IBAction func backToRootButtonTapped(_ sender: UIButton) { guard let rootVC = findTargetRootVC(from: self) else { return } // 让外层导航pop到RootVC rootVC.navigationController?.popToViewController(rootVC, animated: true) // 清空内层导航栈 navigationController?.popToRootViewController(animated: true) } // 递归查找目标RootViewController private func findTargetRootVC(from currentVC: UIViewController) -> RootViewController? { // 如果当前就是RootVC,直接返回 if let rootVC = currentVC as? RootViewController { return rootVC } // 遍历子VC查找 for childVC in currentVC.children { if let foundVC = findTargetRootVC(from: childVC) { return foundVC } } // 向上找父VC return currentVC.parent != nil ? findTargetRootVC(from: currentVC.parent!) : nil } }
各方法优缺点对比
| 方法 | 优点 | 缺点 |
|---|---|---|
| 闭包回调 | 低耦合、逻辑清晰、易维护 | 需要在VC间传递闭包,层级深时稍麻烦 |
| 通知中心 | 跨层级通信方便 | 易造成通知泛滥,需注意内存泄漏 |
| 层级遍历 | 实现直接,无需提前绑定 | 依赖视图结构,维护性差 |
内容的提问来源于stack exchange,提问作者user3239711
相关产品推荐
相关产品推荐

