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

iOS开发:dismiss子控制器后如何保留父控制器原状态?

解决Present/Dismiss后父控制器状态丢失的问题

首先得说,你遇到的这个情况其实不太符合iOS控制器present/dismiss的常规行为——正常来说,present只是把子控制器叠在父控制器的视图层级上,dismiss后父控制器的实例应该完全保留,属性不会被重置。所以核心问题大概率是你的父控制器被意外销毁并重新初始化了,或者代码里有某些逻辑导致状态被重置。

我给你梳理几个排查和解决的方向:

1. 先确认父控制器是不是真的被重建了

先给ParentViewController加个deinit方法打印日志,看看dismiss的时候父控制器是不是被销毁了:

deinit {
    print("ParentViewController 被销毁了!")
}

如果dismiss的时候控制台输出了这句话,那说明你的父控制器实例确实被回收了,之后触发的viewWillAppear其实是新创建的父控制器实例的生命周期方法——这时候你得去查代码里哪里不小心重新初始化了ParentViewController,比如:

  • 是不是在获取TestViewController的时候,误操作加载了父控制器的新实例?
  • 你的父控制器是不是嵌入在某个容器(比如导航控制器、TabBar控制器)里,容器的逻辑导致它被销毁重建?
  • 有没有遇到内存警告,系统自动回收了后台的父控制器?

2. 修正Present/Dismiss的调用逻辑

看你的代码,子控制器的dismissController闭包里调用了self.dismiss——这里的self是父控制器,这个逻辑是对的(因为present操作是父控制器发起的,由父控制器调用dismiss才能正确关闭子控制器)。但要确认子控制器内部有没有额外调用self.dismiss(),如果两边都调用,可能会导致层级混乱,不过一般不会直接引发状态丢失,但还是要排查。

另外,检查你获取TestViewController的方式:如果是从Storyboard加载,要确保是正确的实例,比如:

// 正确的方式:直接实例化子控制器
let presentViewController = storyboard?.instantiateViewController(withIdentifier: "TestViewController") as! TestViewController

不要不小心加载了父控制器的新实例,这是新手常犯的错误。

3. 持久化关键状态(应对系统回收场景)

如果排查后发现是内存警告导致系统回收了父控制器,那就要把需要保留的状态(比如items、screenLoaded)持久化保存。这里推荐用UserDefaults(适合轻量数据),或者CoreData(适合大量数据)。

举个用UserDefaults的例子,首先让你的Item类遵循Codable协议(这样才能序列化):

struct Item: Codable {
    // 你的Item属性
    let id: String
    // ...其他属性
}

然后在父控制器里添加状态保存和恢复的逻辑:

class ParentViewController: UIViewController {
    private var items = [String:[Item]]()
    private var screenLoaded: Bool = false

    override func viewDidLoad() {
        super.viewDidLoad()
        // 从UserDefaults恢复状态
        restoreState()
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        // 保存状态到UserDefaults
        saveState()
    }

    private func saveState() {
        // 保存screenLoaded
        UserDefaults.standard.set(screenLoaded, forKey: "ParentScreenLoaded")
        // 保存items(需要Item遵循Codable)
        do {
            let data = try JSONEncoder().encode(items)
            UserDefaults.standard.set(data, forKey: "ParentItems")
        } catch {
            print("保存items失败:\(error)")
        }
    }

    private func restoreState() {
        screenLoaded = UserDefaults.standard.bool(forKey: "ParentScreenLoaded")
        if let data = UserDefaults.standard.data(forKey: "ParentItems") {
            do {
                items = try JSONDecoder().decode([String:[Item]].self, from: data)
            } catch {
                print("恢复items失败:\(error)")
                items = [:]
            }
        }
    }

    // ...其他代码
}

这样即使父控制器被系统回收,下次重建时也能恢复之前的状态。

4. 检查全局变量/属性的初始化逻辑

最后再确认一下你的items和screenLoaded的初始化逻辑——有没有在其他地方(比如viewWillAppear)不小心把它们重置为初始值?比如如果viewWillAppear里有items = [:]这种代码,那每次触发都会清空,你提供的代码里没有,但还是要排查整个文件的其他逻辑。


内容的提问来源于stack exchange,提问作者thus

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:03:45