如何在模态重开UINavigationController时保留之前的状态
保留模态导航控制器状态并复用的方案
方案一:直接持有实例,复用整个导航栈
这是最简单直接的思路——不让模态的导航控制器被销毁,关闭后把它存在内存里,下次点按钮直接重新present出来:
在触发模态的父控制器里加个属性存导航控制器:
private var persistentNavController: UINavigationController?按钮点击时判断是否已有实例:
@IBAction func showModalButtonTapped(_ sender: UIButton) { if let existingNav = persistentNavController { // 复用现成的,直接弹出来 present(existingNav, animated: true) } else { // 第一次从Storyboard初始化,存起来再弹出 let storyboard = UIStoryboard(name: "Main", bundle: nil) let newNav = storyboard.instantiateViewController(withIdentifier: "你的导航控制器ID") as! UINavigationController persistentNavController = newNav present(newNav, animated: true) } }处理关闭逻辑:
- 用户选完项后,让子控制器通知父控制器调用
dismiss(animated:)就行,父控制器手里攥着引用,导航栈的所有页面都不会被释放,状态完全保留。 - 系统自带的下滑关闭(Sheet样式)会自动触发dismiss,但只要父控制器的强引用还在,导航控制器就不会被回收,下次present时还是原来的栈状态。
- 用户选完项后,让子控制器通知父控制器调用
小贴士:如果担心内存占用,比如用户离开当前父页面后不需要保留状态,可以在
viewWillDisappear里把persistentNavController设为nil,释放掉资源。
方案二:保存栈状态,下次重建导航栈
要是不想一直占着内存存整个导航控制器,可以手动记录每个页面的关键信息,下次打开时重新拼出导航栈:
先定义个结构体存每个页面的状态(比如页面ID和需要的参数):
struct ControllerState: Codable { let vcID: String // Storyboard里给每个页面设的restorationIdentifier let params: [String: Codable] // 页面需要的参数,得是能序列化的类型 }在导航栈的根表视图控制器里,监听导航栈变化,把状态存到父控制器:
override func viewDidLoad() { super.viewDidLoad() navigationController?.delegate = self } extension YourRootTableVC: UINavigationControllerDelegate { func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) { // 遍历当前导航栈,生成状态数组 let stackStates = navigationController.viewControllers.compactMap { vc -> ControllerState? in guard let vcID = vc.restorationIdentifier else { return nil } // 这里根据实际页面类型取参数,比如你推的子页面存了选中的数据 let params: [String: Codable] if let childVC = vc as? YourChildVC { params = ["selectedItem": childVC.selectedItemID] } else { params = [:] } return ControllerState(vcID: vcID, params: params) } // 把状态传给父控制器存起来 if let parentVC = presentingViewController as? YourParentVC { parentVC.savedStackStates = stackStates } } }下次打开模态时,根据保存的状态重建导航栈:
@IBAction func showModalButtonTapped(_ sender: UIButton) { let storyboard = UIStoryboard(name: "Main", bundle: nil) let navController = storyboard.instantiateViewController(withIdentifier: "你的导航控制器ID") as! UINavigationController // 如果有保存的栈状态,就挨个初始化页面拼栈 if !savedStackStates.isEmpty { var newVCs: [UIViewController] = [] for state in savedStackStates { let vc = storyboard.instantiateViewController(withIdentifier: state.vcID) // 给页面设置参数,对应上面存的内容 if let childVC = vc as? YourChildVC { childVC.selectedItemID = state.params["selectedItem"] as? String } newVCs.append(vc) } navController.setViewControllers(newVCs, animated: false) } present(navController, animated: true) }
为什么State Preservation不适合你的需求
State Preservation 是给应用重启后恢复状态用的,它会把状态写到磁盘里,而你要的只是应用运行期间保留,不需要跨重启。而且它配置起来麻烦,每个页面都要设restorationIdentifier,还要写编码解码逻辑,完全没必要用在这个场景。
内容的提问来源于stack exchange,提问作者Runt8
相关产品推荐
相关产品推荐

