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

Swift 4中如何在present UIViewController时实现缩放动画?

实现ViewController的缩放Present动画

我来帮你搞定这个缩放present动画的问题!你之前用CATransition的方式没生效,主要是因为这种方法和系统的ViewController转场流程不太匹配,直接给window的layer加动画没法完全接管VC的转场逻辑。下面给你两种解决方案,优先推荐第一种官方规范的自定义转场方式:

方法一:自定义转场动画(推荐,更灵活可控)

iOS 7之后官方提供了UIViewControllerTransitioningDelegate协议,专门用来实现自定义的VC转场动画,这是最规范的做法。

第一步:写一个缩放动画的实现类

创建一个遵循UIViewControllerAnimatedTransitioning的类,专门处理缩放动画的逻辑:

class ScalePresentAnimator: NSObject, UIViewControllerAnimatedTransitioning {
    // 动画时长,可根据需求调整
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.5
    }
    
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        // 获取要present的ViewController和它的view
        guard let targetVC = transitionContext.viewController(forKey: .to),
              let targetView = transitionContext.view(forKey: .to) else {
            transitionContext.completeTransition(false)
            return
        }
        
        // 设置动画初始状态:缩小到30%,完全透明
        targetView.transform = CGAffineTransform(scaleX: 0.3, y: 0.3)
        targetView.alpha = 0.0
        
        // 将目标view添加到转场容器中
        let container = transitionContext.containerView
        container.addSubview(targetView)
        
        // 执行缩放+渐显动画,还可以加弹簧效果让动画更自然
        UIView.animate(withDuration: transitionDuration(using: transitionContext),
                       delay: 0,
                       usingSpringWithDamping: 0.8,
                       initialSpringVelocity: 0.5,
                       options: .curveEaseInOut,
                       animations: {
            // 恢复到原始大小和透明度
            targetView.transform = .identity
            targetView.alpha = 1.0
        }, completion: { finished in
            // 通知系统转场完成
            transitionContext.completeTransition(finished && !transitionContext.transitionWasCancelled)
        })
    }
}

第二步:在源ViewController中设置转场代理

在你要触发present的ViewController里,遵循UIViewControllerTransitioningDelegate,并关联刚才写的动画类:

class YourSourceVC: UIViewController, UIViewControllerTransitioningDelegate {
    // 初始化缩放动画对象
    private let scaleAnimator = ScalePresentAnimator()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // 记得给按钮绑定点击事件,比如:
        // yourButton.addTarget(self, action: #selector(btnClickView(_:)), for: .touchUpInside)
    }
    
    @objc func btnClickView(_ sender: UIButton) {
        let objImagePreview = ImagePreview(nibName: "ImagePreview", bundle: nil)
        // 设置转场代理为当前VC
        objImagePreview.transitioningDelegate = self
        // 必须设置模态呈现样式为自定义,否则系统会用默认转场
        objImagePreview.modalPresentationStyle = .custom
        // 这里animated要设为true,让系统触发自定义动画
        self.present(objImagePreview, animated: true, completion: nil)
    }
    
    // 代理方法:返回present时的动画对象
    func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return scaleAnimator
    }
    
    // 如果需要dismiss时的反向缩放动画,再加这个方法:
    // func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    //     // 可以写一个反向的动画类,或者修改现有类的逻辑
    //     return scaleDismissAnimator
    // }
}

方法二:修复你原来的CATransition方式(不推荐,仅作参考)

如果你想继续用CATransition,可以调整动画的添加位置,不过这种方法不是官方推荐的,可能会有兼容性问题:

@objc func btnClickView(_ sender: UIButton) {
    let objImagePreview = ImagePreview(nibName: "ImagePreview", bundle: nil)
    
    let transition = CATransition()
    transition.duration = 1
    // 把类型改成scale,而不是moveIn,这样才是缩放效果
    transition.type = .scale
    transition.subtype = kCATransitionFromTop
    transition.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
    
    // 把动画添加到当前VC的view.layer上,而不是window
    self.view.layer.add(transition, forKey: kCATransition)
    
    self.present(objImagePreview, animated: false, completion: nil)
}

这种方法的问题是:动画是加在当前VC的view上,不是真正的VC转场动画,dismiss的时候不会自动有反向动画,而且在某些iOS版本可能出现异常。

重要提示

  • 一定要把目标VC的modalPresentationStyle设为.custom,否则系统会忽略你的自定义转场代理,用默认的present动画。
  • 自定义转场方式可以轻松扩展,比如添加dismiss的反向动画、调整动画参数,适配不同的需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:45:22