模态弹出后导航返回根控制器时的调用不平衡警告解决方案问询
解决“Unbalanced calls to begin/end appearance transitions”警告并实现需求
这个警告的根源在于你在present动画完成的回调里立刻执行了popToRootViewController——此时模态视图控制器的外观过渡还没完全收尾,同时触发导航栈的pop操作,导致两个视图过渡逻辑冲突,系统就抛出了这个不平衡调用的警告。
要实现“用户关闭模态视图后回到导航栈根控制器”的需求,我们应该把pop操作延迟到模态视图被关闭之后执行,而不是在present完成时就触发。这里有几种优雅的实现方式:
方法1:使用闭包回调(推荐)
这种方式耦合度低,逻辑清晰,适配大多数场景:
首先在你的MyViewController里添加一个闭包属性,用于传递dismiss完成后的回调逻辑:
class MyViewController: UIViewController { // 定义dismiss完成后的回调闭包 var onDismissCompletion: (() -> Void)? // 假设你有一个关闭按钮,点击后触发dismiss @IBAction func closeButtonTapped(_ sender: UIButton) { dismiss(animated: true) { // 在dismiss动画完全结束后执行回调 self.onDismissCompletion?() } } // 处理系统自带的下拉dismiss(比如iOS 13+的sheet模态) override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) if isBeingDismissed { onDismissCompletion?() } } }
然后在tableView的点击事件里,给闭包赋值,指定dismiss后执行pop操作:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let vc = MyViewController() // 弱引用导航控制器,避免循环引用 weak var navController = self.navigationController vc.onDismissCompletion = { navController?.popToRootViewController(animated: true) } present(vc, animated: true) }
方法2:在模态VC的消失方法中直接处理
如果你不想用闭包,也可以在MyViewController的消失回调里直接操作原导航栈:
class MyViewController: UIViewController { override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) // 确认当前是被dismiss,而不是push到其他VC if isBeingDismissed { // 找到原导航控制器并pop到根 if let presentingNav = presentingViewController as? UINavigationController { presentingNav.popToRootViewController(animated: true) } else if let presentingNav = presentingViewController?.navigationController { presentingNav.popToRootViewController(animated: true) } } } }
这种方式不需要修改present的代码,但耦合度稍高,适合简单场景。
为什么原代码会出问题?
原代码在present的completion里立刻执行pop,此时MyViewController的viewDidAppear生命周期还在处理中,系统正在处理它的外观过渡;而pop操作会触发原导航栈中当前VC的消失,两个过渡操作同时进行,导致系统的beginAppearanceTransition和endAppearanceTransition调用不匹配,从而抛出警告。
内容的提问来源于stack exchange,提问作者Anthony Dito
相关产品推荐
相关产品推荐

