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

如何在自定义转场动画执行中途中断并关闭视图控制器?

解决自定义转场中点击调光视图取消转场的问题

核心问题分析

当前代码的两个关键问题:

  1. UIDimmingView的hitTest方法返回nil,导致点击调光视图本身时无法触发手势识别
  2. 转场过程中直接调用dismiss会和正在进行的转场动画冲突,需要先中断当前动画再完成关闭操作

步骤1:修复调光视图的点击响应

修改UIDimmingView的hitTest方法,让它能响应自身的点击:

class UIDimmingView: UIView {
    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        let hitView = super.hitTest(point, with: event)
        // 点击自身时返回self,确保手势能捕捉到点击事件
        return hitView == self ? self : hitView
    }
}

步骤2:将手势绑定到调光视图

把点击手势直接添加到dimmingView上,而非容器视图,确保只有点击调光区域才触发取消逻辑:

lazy var tapGesture: UITapGestureRecognizer = {
    let gesture = UITapGestureRecognizer(target: self, action: #selector(onTap(_:)))
    return gesture
}()

lazy var dimmingView: UIDimmingView = {
    let view = UIDimmingView()
    view.backgroundColor = .black.withAlphaComponent(0.4)
    view.alpha = 0.0
    view.addGestureRecognizer(self.tapGesture) // 绑定手势到调光视图
    return view
}()

步骤3:同步转场动画并支持中断

在转场开始时,使用转场协调器同步调光视图和弹窗的动画;同时在自定义转场动画控制器中添加取消标记,处理动画中断逻辑:

更新转场准备方法

override func presentationTransitionWillBegin() {
    guard let containerView, let presentedView,
          let coordinator = presentingViewController.transitionCoordinator else { return }

    containerView.insertSubview(self.dimmingView, at: 0)
    self.dimmingView.fillSuperview()

    presentedView.layer.cornerRadius = 20.0
    presentedView.layer.cornerCurve = .continuous
    presentedView.layer.masksToBounds = true
    
    // 用转场协调器同步调光视图的淡入动画
    coordinator.animate(alongsideTransition: { _ in
        self.dimmingView.alpha = 1.0
    }, completion: nil)
}

自定义转场动画控制器(添加取消逻辑)

class CustomPresentationAnimator: NSObject, UIViewControllerAnimatedTransitioning {
    var shouldCancelTransition = false
    weak var dimmingView: UIDimmingView? // 弱引用调光视图
    
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.3
    }
    
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        guard let toVC = transitionContext.viewController(forKey: .to),
              let toView = transitionContext.view(forKey: .to) else { return }
        
        let containerView = transitionContext.containerView
        containerView.addSubview(toView)
        
        // 初始状态:弹窗缩小+透明
        toView.transform = CGAffineTransform(scaleX: 0.9, y: 0.9)
        toView.alpha = 0
        
        UIView.animate(withDuration: transitionDuration(using: transitionContext),
                       delay: 0,
                       options: .curveEaseInOut) {
            if self.shouldCancelTransition {
                // 取消转场:回到初始透明缩小状态
                toView.transform = CGAffineTransform(scaleX: 0.9, y: 0.9)
                toView.alpha = 0
                self.dimmingView?.alpha = 0
            } else {
                // 正常转场:恢复弹窗到正常状态
                toView.transform = .identity
                toView.alpha = 1
            }
        } completion: { [weak self] finished in
            guard let self = self else { return }
            if self.shouldCancelTransition {
                // 标记转场失败,然后关闭弹窗
                transitionContext.completeTransition(false)
                toVC.dismiss(animated: false)
            } else {
                transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
            }
            // 重置取消标记
            self.shouldCancelTransition = false
        }
    }
}

步骤4:处理点击事件

在点击回调中设置取消标记,触发动画中断:

@objc private func onTap(_ sender: UITapGestureRecognizer) {
    // 假设animator是你的自定义转场动画控制器实例
    self.animator.shouldCancelTransition = true
    // 强制触发动画更新
    UIView.animate(withDuration: 0.2) {
        self.dimmingView.alpha = 0
        self.presentedViewController.view.transform = CGAffineTransform(scaleX: 0.9, y: 0.9)
        self.presentedViewController.view.alpha = 0
    } completion: { _ in
        self.presentedViewController.dismiss(animated: false)
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 03:43:13