You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在模态重开UINavigationController时保留之前的状态

保留模态导航控制器状态并复用的方案

方案一:直接持有实例,复用整个导航栈

这是最简单直接的思路——不让模态的导航控制器被销毁,关闭后把它存在内存里,下次点按钮直接重新present出来:

  1. 在触发模态的父控制器里加个属性存导航控制器:

    private var persistentNavController: UINavigationController?
    
  2. 按钮点击时判断是否已有实例:

    @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)
        }
    }
    
  3. 处理关闭逻辑:

    • 用户选完项后,让子控制器通知父控制器调用dismiss(animated:)就行,父控制器手里攥着引用,导航栈的所有页面都不会被释放,状态完全保留。
    • 系统自带的下滑关闭(Sheet样式)会自动触发dismiss,但只要父控制器的强引用还在,导航控制器就不会被回收,下次present时还是原来的栈状态。

小贴士:如果担心内存占用,比如用户离开当前父页面后不需要保留状态,可以在viewWillDisappear里把persistentNavController设为nil,释放掉资源。

方案二:保存栈状态,下次重建导航栈

要是不想一直占着内存存整个导航控制器,可以手动记录每个页面的关键信息,下次打开时重新拼出导航栈:

  1. 先定义个结构体存每个页面的状态(比如页面ID和需要的参数):

    struct ControllerState: Codable {
        let vcID: String // Storyboard里给每个页面设的restorationIdentifier
        let params: [String: Codable] // 页面需要的参数,得是能序列化的类型
    }
    
  2. 在导航栈的根表视图控制器里,监听导航栈变化,把状态存到父控制器:

    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
            }
        }
    }
    
  3. 下次打开模态时,根据保存的状态重建导航栈:

    @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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 14:37:19