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

Swift4无Storyboard/导航控制器时,如何无异常动画dismiss两个模态控制器

Swift 4:从模态链C直接回到A且动画正常的解决方案

我完全理解你遇到的这个动画问题——用链式调用self.presentingViewController?.presentingViewController?.dismiss()确实能实现跳转,但动画会出现C直接消失、只有B执行退场动画的突兀效果,这是因为系统默认只会对直接被dismiss的控制器(也就是B)执行动画,而C作为B的子模态控制器会被直接移除。下面给你两个靠谱的解决方案,都能实现平滑的从C回到A的动画:

方案一:连续dismiss并衔接动画(简单易实现)

这个方案是先让C退场到B,再在C退场动画完成后让B退场到A,两个动画连贯执行,视觉上不会有突兀感:

// 在ViewController C中的返回按钮或触发事件里添加这段代码
self.dismiss(animated: true) {
    // 等C的退场动画完成后,让B也退场到A
    self.presentingViewController?.dismiss(animated: true, completion: nil)
}

这种方法的优势是不需要额外配置,完全用系统默认动画,只是把两个dismiss操作通过completion回调衔接起来,用户看到的是连续的退场动画,不会出现控件突然消失的问题。

方案二:自定义转场动画(实现直接从C到A的单步动画)

如果你希望实现直接从C跳回A的单步退场动画(不需要经过B的过渡),可以自定义转场动画。步骤如下:

  1. 首先在ViewController C中遵守UIViewControllerTransitioningDelegate协议:
class ViewControllerC: UIViewController, UIViewControllerTransitioningDelegate {
    override func viewDidLoad() {
        super.viewDidLoad()
        self.transitioningDelegate = self
    }
}
  1. 实现自定义的退场动画控制器(这里模拟系统从下到上present的反向动画,即从上到下退场):
class CustomDismissAnimator: NSObject, UIViewControllerAnimatedTransitioning {
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.35 // 和系统默认动画时长一致
    }
    
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        guard let fromVC = transitionContext.viewController(forKey: .from),
              let toVC = transitionContext.viewController(forKey: .to) else {
            transitionContext.completeTransition(false)
            return
        }
        
        let containerView = transitionContext.containerView
        containerView.insertSubview(toVC.view, belowSubview: fromVC.view)
        
        // 自定义退场动画:让C的视图从上滑出屏幕
        let screenHeight = UIScreen.main.bounds.height
        UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: {
            fromVC.view.frame.origin.y = screenHeight
        }) { _ in
            transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
        }
    }
}
  1. 在ViewController C中实现转场代理方法,返回自定义动画控制器:
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    return CustomDismissAnimator()
}
  1. 最后,在触发返回的事件中,直接获取到A并调用dismiss:
// 递归找到最底层的presenting控制器(也就是A)
func rootPresentingViewController() -> UIViewController? {
    var currentVC = self.presentingViewController
    while let nextVC = currentVC?.presentingViewController {
        currentVC = nextVC
    }
    return currentVC
}

// 触发返回的方法
func goBackToA() {
    rootPresentingViewController()?.dismiss(animated: true, completion: nil)
}

这种方法的优势是能实现完全自定义的退场动画,直接从C过渡到A,没有中间B的显示,动画效果更符合你的需求。

补充说明

为什么之前的代码会出现动画异常?

当你调用self.presentingViewController?.presentingViewController?.dismiss()时,本质是让A控制器dismiss它的presented控制器(也就是B),此时C作为B的presented控制器会被系统直接移除,不会执行退场动画,所以会出现C突然消失、只有B执行动画的异常效果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 09:07:18