iOS dismiss视图控制器后刷新前序页面UI触发nil崩溃问题
根因说明
崩溃和视图是否在焦点状态无关,核心错误是你在B的dismiss回调中,手动let vc = WellnessPlanEnrollmentController()初始化了一个全新的、和当前页面栈完全无关的A控制器实例。
这个新创建的实例没有经过storyboard视图加载流程,所有通过IBOutlet绑定的UI控件(包括你的_petNameLabel)都是未初始化的nil状态,此时调用refreshView访问隐式解包的label属性,必然触发Unexpectedly found nil while implicitly unwrapping an Optional value崩溃。
修复方法
选任意一种即可:
- 方案1:直接获取系统持有的presenting控制器
B在dismiss时,系统自带的presentingViewController属性就是原来present它的A实例,直接拿这个实例调用刷新方法即可,不要自己新建对象:// 替换你B里原来的dismiss代码 self.dismiss(animated: true) { [weak self] in guard let realVC_A = self?.presentingViewController as? WellnessPlanEnrollmentController else { return } realVC_A.refreshView() } - 方案2:闭包/代理回调(推荐,耦合度更低)
不要让B直接依赖A的类名,通过回调把事件传给A,由A自己触发刷新,是更规范的页面间通信方式:- 给B控制器(PopoutWindow)添加回调属性:
class PopoutWindow: UIViewController { var finishHandler: (() -> Void)? // 你原来的其他代码不变 } - A在present B之前,给回调赋值刷新逻辑:
@IBAction func presentView(_ sender: Any) { let storyBoard : UIStoryboard = UIStoryboard(name: "main", bundle:nil) let vc = storyBoard.instantiateViewController(withIdentifier: "Popout") as! PopoutWindow vc.modalTransitionStyle = .coverVertical vc.modalPresentationStyle = .overFullScreen // 绑定回调 vc.finishHandler = { [weak self] in self?.refreshView() } self.present(vc, animated:true, completion:nil) } - B操作完成dismiss时触发回调即可:
self.dismiss(animated: true) { self.finishHandler?() }
- 给B控制器(PopoutWindow)添加回调属性:
额外注意点
- 你当前用的
overFullScreen展示样式,dismiss时A不会被销毁,视图也一直存在,只要拿到的是真实的A实例,IBOutlet控件不会是nil - 如果后续遇到需要提前给控制器传数据、但视图还没加载的场景,不要直接给label赋值,先把数据存在普通属性里,等
viewDidLoad执行时再把数据赋值给UI控件,避免提前访问未加载的UI属性触发崩溃
内容的提问来源于stack exchange,提问作者curiousSloth
相关产品推荐
相关产品推荐

