如何让子UIViewController dismiss后触发父控制器completion handler
实现子控制器dismiss后触发父控制器回调的方案
首先明确:present(_:animated:completion:) 方法传入的completion闭包,触发时机是present动画执行完毕、子控制器完全展示在顶层时,和子控制器后续的dismiss操作无关,你之前的预期和UIKit的实际设计不符。
你可以根据项目的最低支持版本,选下面两种成熟方案实现需求:
方案1:iOS 15+ 原生无侵入方案(推荐)
iOS 15 开始系统提供了专门的dismiss完成回调,不需要修改子控制器的代码,只要在父控制器中遵守UIAdaptivePresentationControllerDelegate协议,实现对应方法即可:
// 父控制器中present子控制器的代码 override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) // 数据缺失判断逻辑 if 数据缺失 { let myUpload = UploadInv() myUpload.modalPresentationStyle = = .fullScreen myUpload.modalTransitionStyle = .flipHorizontal // 绑定代理 myUpload.presentationController?.delegate = self x += 1 // 这里不需要传present的completion,除非你要处理present完成的逻辑 self.present(myUpload, animated: true) } } // 实现代理回调,子控制器dismiss动画完成后会自动触发 extension 你的父控制器类名: UIAdaptivePresentationControllerDelegate { func presentationControllerDidDismiss(_ presentationController: UIPresentationController) { print("\(x)") // 在这里写dismiss完成后要执行的业务逻辑,比如刷新数据、读取子控制器回传的内容 } }
这个回调对所有dismiss场景生效:不管是子控制器主动调用dismiss方法关闭,还是用户通过下拉手势关闭模态页,都会在dismiss动画完全结束后触发,时机准确。
方案2:自定义闭包属性(兼容全系统版本,可控性最高)
如果需要支持iOS15以下的系统版本,直接用经典的闭包逆传方案即可,逻辑完全可控,还可以通过闭包参数直接回传子控制器采集到的业务数据:
- 首先在子控制器
UploadInv中定义回调属性,在自身dismiss完成时触发闭包:
class UploadInv: UIViewController { // 定义dismiss完成回调,可根据业务需要加参数,比如 var onDismissComplete: ((UploadData) -> Void)? var onDismissComplete: (() -> Void)? // 子控制器中触发关闭的逻辑(比如关闭按钮点击方法) @objc private func closeAction() { self.dismiss(animated: true) { [weak self] in // dismiss动画完成后再调用回调,保证时机准确 self?.onDismissComplete?() } } }
- 在父控制器present子控制器时,给回调属性赋值即可:
override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) // 数据缺失判断逻辑 if 数据缺失 { let myUpload = UploadInv() myUpload.modalPresentationStyle = .fullScreen myUpload.modalTransitionStyle = .flipHorizontal x += 1 // 绑定dismiss完成回调 myUpload.onDismissComplete = { [weak self] in guard let self = self else { return } print("\(self.x)") // 执行后续业务逻辑 } self.present(myUpload, animated: true) } }
避坑提示
- 你贴的测试代码有个笔误:前面实例化的变量名是
myUp,后面配置属性的时候用了myUpload,实际运行会触发未声明标识符的编译错误,记得统一变量名。 - 不要尝试通过重写父控制器的
dismiss(animated:completion:)方法监听关闭事件,子控制器调用dismiss时系统会自动沿控制器层级转发事件,很容易出现时机提前、重复触发的问题。
内容的提问来源于stack exchange,提问作者Bartender1382
相关产品推荐
相关产品推荐

