Present ViewControllerB后如何dismiss ViewControllerA?(无Storyboards)
看起来你遇到了控制器层级的典型问题——你想在B加载完成后关掉A,但因为A是B的presenting控制器,直接在A里调用dismiss反而会把B给关掉。我来帮你理清问题原因,再给出几种可行的解决方案:
为什么你的代理方法会失效?
先拆解下你的控制器层级:假设当前是父控制器 → present → ViewControllerA → present → ViewControllerB。当你在A的代理回调里调用self.dismiss(animated: true)时,这个方法的作用是dismiss掉A自己present出来的控制器(也就是B),而不是dismiss A本身。只有A的presenting控制器(也就是那个最初present A的父控制器)才能dismiss A,但如果直接让父控制器dismiss A,B作为A的presented控制器也会被系统自动关掉——这就是你当前困境的核心。
解决方案
根据你的需求(B加载完成后保留,同时移除A),推荐以下两种优雅的实现方式:
方案1:让父控制器直接Present B,再Dismiss A
这是最直观的方法,绕开A作为中间层的限制,让最初present A的父控制器直接present B,然后再关掉A:
在ViewControllerA中,替换你原来present B的代码:
let vcB = ViewControllerB() // 让A的presenting控制器(父控制器)来present B self.presentingViewController?.present(vcB, animated: true) { // 等B完全显示后,dismiss掉A self.dismiss(animated: false) }
这样B的presenting控制器变成了父控制器,A被dismiss后不会影响B的显示。
方案2:用视图转场替换A为B
如果不想依赖父控制器的存在,可以用系统的转场方法直接把A的视图替换成B的视图,同时调整控制器层级:
在ViewControllerA中:
let vcB = ViewControllerB() vcB.view.frame = self.view.bounds // 将B添加到A的presenting控制器的子控制器列表 self.presentingViewController?.addChild(vcB) // 执行转场动画,替换视图 UIViewController.transition(from: self, to: vcB, duration: 0.3, options: .transitionCrossDissolve) { _ in // 完成转场后,更新控制器层级 vcB.didMove(toParent: self.presentingViewController) self.removeFromParent() // 最后dismiss掉A,此时B已经和父控制器绑定,不会被影响 self.dismiss(animated: false) }
这种方法可以实现无缝的视图切换,同时保证B的生命周期独立于A。
额外提醒
如果你坚持要用代理模式,那需要在B的回调里先把B的presenting控制器切换成A的父控制器,再dismiss A,但这种写法比较hack,不推荐:
// 在ViewControllerB的viewDidAppear中 override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) guard let parentOfA = self.presentingViewController?.presentingViewController else { return } // 让A的父控制器重新present B parentOfA.present(self, animated: false) { // 此时B的presenting控制器是父控制器,再dismiss A self.presentingViewController?.dismiss(animated: true) } }
内容的提问来源于stack exchange,提问作者pavlos

