前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >iOS_Custom Transition Animation 自定义转场动画

iOS_Custom Transition Animation 自定义转场动画

作者头像
mikimo
发布2023-10-18 14:52:01
3030
发布2023-10-18 14:52:01
举报
文章被收录于专栏:iOS开发~

1、push-pop 动画协议

想要在 pushpop viewController 时使用自定义的转场动效,需要设置self.naviagtionController.delegate, 并实现UINavigationControllerDelegate的一个方法:

代码语言:javascript
复制
// 返回一个实现了转场动画协议的对象
func navigationController(_ navigationController: UINavigationController,
                            animationControllerFor operation: UINavigationController.Operation,
                            from fromVC: UIViewController,
                            to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    if operation == .push {
        return a push animator // 实现push动画的对象
    } 
    if operation == .pop {
        return a pop animator // 实现pop动画的对象
    } 
}

2、present-dismiss 动画协议

想要在 presentdismiss viewController 时使用自定义的转场动效,需要设置toViewController.transitioningDelegate, 并实现UIViewControllerTransitioningDelegate协议的两个方法:

代码语言:javascript
复制
// 返回一个实现了 present 转场动画协议的对象
func animationController(forPresented presented: UIViewController,
                         presenting: UIViewController,
                         source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    return a present animator // 实现 present 动画的对象
}
// 返回一个实现了 dismiss 转场动画协议的对象
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    return a pop animator // 实现 dismiss 动画的对象
}

Tips: 这个协议谁实现都可以:fromVC or toVC or new an object, as you like.


3、实现转场动画协议

以上2个协议返回的4个animator都是实现了UIViewControllerAnimatedTransitioning协议的对象。举例实现如下:

3.1 动画时长

代码语言:javascript
复制
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
    return 0.5  // 返回动画时长
}

3.2 push or present animation (显示动画)

执行动画的方法animateTransition,带了一个遵循UIViewControllerContextTransitioning协议的transitionContext参数。具体可以取到哪些数据详情可见UIViewControllerContextTransitioning。 以下列举一些常用的:

代码语言:javascript
复制
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
    // 1. get data for animation (获取动画需要的数据)
    // animation contianer (动画容器)
    let containerView = transitionContext.containerView
    
    // come from viewController (来源页面的)
    // viewController
    let fromVC = transitionContext.viewController(forKey: .from)
    // view
    let fromView = transitionContext.view(forKey: .from)
    // 初始frame
    let fromViewInitialFrame = transitionContext.initialFrame(for: fromVC)
    // 最终frame
    let fromViewFinalFrame = transitionContext.finalFrame(for: fromVC)
    
    // to viewController (跳转页面的)
    // viewController
    let toVC = transitionContext.viewController(forKey: .to)
    // view
    let toView = transitionContext.view(forKey: .to)
    // 初始frame
    var toViewInitialFrame = transitionContext.initialFrame(for: toVC)
    // 最终frame
    let toViewFinalFrame = transitionContext.finalFrame(for: toVC)
    
    // and so on ...... 
    // do animation with available data (根据拿到的数据做动画)
    
    // 2. calculate the value what you want (计算初始位置+最终位置)
    toViewInitialFrame.origin.x = containerFrame.size.width;
    toViewInitialFrame.origin.y = containerFrame.size.height;
    
    // 3. Add do toView to the contenerView, and set the initial value (添加 toView 到 contianerView 上, 并设置初始值)
    containerView.addSubview(toView)
    toView.frame = toViewInitialFrame;
    
    // Add additional views required for animation and set initial values
    // 添加动画所需的其他视图并设置初始值
    ...... 

    // 4. execute animation 执行动画
    UIView.animate(withDuration: self.transitionDuration(using: transitionContext)) {
        // 5.1 set final frame for animation view
        toView.frame = toViewFinalFrame
        
        // Set additional views final values
        ......
    
    } completion: { finish in
        // 5.2 get animation result
        let success = !transitionContext.transitionWasCancelled
        // 5.2.1 remove the view if animation fail
        if !success {
            toView.removeFromSuperview()
        }
        // 5.2.1 callback animation result
        transitionContext.completeTransition(success)
    }
}

3.3 动画结束

动画结束方法:

代码语言:javascript
复制
func animationEnded(_ transitionCompleted: Bool) {
    // transitionCompleted 动画执行结果: YES-success NO-fail
}

3.4 pop or dismiss animation (消失动画)

大致跟显示动画一致,转场动画都是需要显示toView, 让fromView消失

  1. 上面显示动画的例子: 是将toView加到containerView上,并对齐进行动画。
  • 动画成功:toView就显示在conatinerView上,进入的是下一个页面
  • 动画失败即转场失败:则应该将toView从容器上移除,即还停留在原来的页面上。
  1. 以下消失动画的例子: 同样是将toView加到containerView上,但用的是fromView进行动画。
  • 动画成功:应将fromView从容器上移除,进入下一个页面
  • 动画失败即转场失败:则不会移除fromView,即还停留在原来的页面上。
代码语言:javascript
复制
/// 转场动画
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
    // 1. get data for animation (获取动画需要的数据)
    ......
    let toViewStartFrame = transitionContext.initialFrame(for: toVC)
    var fromViewFinalFrame = transitionContext.finalFrame(for: fromVC)
    
    // 2. calculate the value what you want (计算初始位置+最终位置)
    fromViewFinalFrame = CGRect(x: CGRectGetWidth(containerFrame),
                                y: CGRectGetHeight(containerFrame),
                                width: CGRectGetWidth(fromView.frame),
                                height: CGRectGetHeight(fromView.frame))
    
    // 3. Add do toView to the contenerView, and set the initial value (添加 toView 到 contianerView 上, 并设置初始值)
    containerView.addSubview(toView)
    toView.frame = toViewStartFrame;
    
    // 4. execute animation 执行动画
    UIView.animate(withDuration: self.transitionDuration(using: transitionContext)) {
        // 5.1 set final frame for animation view
        fromView.frame = fromViewFinalFrame

    } completion: { finish in
        // 5.2 get animation result
        let success = !transitionContext.transitionWasCancelled
        // 5.2.1 remove the view after animation finish
        if success {
            fromView.removeFromSuperview()
        }
        transitionContext.completeTransition(success)
    }
}

消失动画里需要注意的是,如果是pop是能拿到toView,但如果是dimiss是拿不到toView的。


4、UIPresentationController

presentdismiss动画如果想在一个中间的viewController进行,则在实现UIViewControllerTransitioningDelegate协议时,不要实现以上2个返回animator的方法,而是实现以下返回UIPresentationController的方法:

代码语言:javascript
复制
// 返回实现 present-dismiss 动效的VC
func presentationController(forPresented presented: UIViewController,
                            presenting: UIViewController?,
                            source: UIViewController) -> UIPresentationController? {
    return a presentation controller // 实现 present-dismiss 动画的对象
}

官方这个例子主要的动画是设置presentVCframeframeOfPresentedViewInContainerViewpresentfinalframe,是dismissinitialFrame。 然后在presentationTransitionWillBegindismissalTransitionWillBegin方法里执行的动画,仅是添加了一个偏暗的背景View,然后调整alpha动画显示消失

效果如如下:


4.1 设置presentVC的frame

代码语言:javascript
复制
// presentVC 在动画容器上的 frame
override var frameOfPresentedViewInContainerView: CGRect {
    get {                    
        let containerBounds: CGRect = self.containerView?.bounds ?? .zero
        let width = CGFloat(floorf(Float(containerBounds.size.width) / 2.0))
        let height = containerBounds.size.height
        let originX = containerBounds.size.width - width
                           
        return CGRect(x: originX, y: 0.0, width: width, height: height)
    }
}

4.2 present 动画

代码语言:javascript
复制
// 暗色背景
lazy var moDimmingView: UIView = {
    let view = UIView(frame: .zero)
    view.backgroundColor = UIColor(white: 0.0, alpha: 0.4)
    view.alpha = 0.0
    return view
}()
代码语言:javascript
复制
// MARK: - 将要开始 present,设置初始值 和 动画回调
override func presentationTransitionWillBegin() {
    super.presentationTransitionWillBegin()    
    // 1. get animation container view (获取动画容器视图)
    guard let containerView = containerView else { return }
    
    // 2. set initial value for animation views and add to container view (设置动画视图的初始值, 并添加到都到容器上)
    self.moDimmingView.frame = containerView.bounds
    self.moDimmingView.alpha = 0.0
    containerView.insertSubview(self.moDimmingView, at: 0)
    
    // 3. execute animation (执行动画)
    // 这里尝试去拿一个时间点的回调,能拿到就在回调里执行显示动画;拿不到就直接设置显示
    guard let transitionCoordinator = self.presentedViewController.transitionCoordinator else {
        self.moDimmingView.alpha = 1.0
        return
    }
    transitionCoordinator.animateAlongsideTransition(in: self.presentedView) { context in
        self.moDimmingView.alpha = 1.0
    }
}

// MARK: - present 动画结束
override func presentationTransitionDidEnd(_ completed: Bool) {
    super.presentationTransitionDidEnd(completed)    
    // remove dark background view when transition fail
    if !completed {
        self.moDimmingView.removeFromSuperview()
    }
}

4.3 dismiss 动画

代码语言:javascript
复制
// MARK: - 将要开始 dismiss,设置初始值 和 动画回调
override func dismissalTransitionWillBegin() {
    super.dismissalTransitionWillBegin()    
    guard let transitionCoordinator = self.presentedViewController.transitionCoordinator else {
        self.moDimmingView.alpha = 0.0
        return
    }
    transitionCoordinator.animateAlongsideTransition(in: self.presentedView) { context in
        self.moDimmingView.alpha = 0.0
    }
}

// MARK: - dismiss 动画结束
override func dismissalTransitionDidEnd(_ completed: Bool) {
    super.dismissalTransitionDidEnd(completed)    
    if completed {
        self.moDimmingView.removeFromSuperview()
    }
}

以上,参照官方的例子,可以根据需要写出想要的动画

Demo:github address

参考: Customizing the Transition Animations Creating Custom Presentations

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-11-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、push-pop 动画协议
  • 2、present-dismiss 动画协议
  • 3、实现转场动画协议
    • 3.1 动画时长
      • 3.2 push or present animation (显示动画)
        • 3.3 动画结束
          • 3.4 pop or dismiss animation (消失动画)
          • 4、UIPresentationController
            • 4.1 设置presentVC的frame
              • 4.2 present 动画
                • 4.3 dismiss 动画
                相关产品与服务
                容器服务
                腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档