iOS_Custom Transition Animation 自定义转场动画
创始人
2024-02-20 17:27:04
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 动画


1、push-pop 动画协议

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

// 返回一个实现了转场动画协议的对象
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协议的两个方法:

// 返回一个实现了 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协议的对象。举例实现如下:

push+present-animation

3.1 动画时长

func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {return 0.5  // 返回动画时长
}

3.2 push or present animation (显示动画)

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

func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {// 1. get data for animation (获取动画需要的数据)// animation contianer (动画容器)let containerView = transitionContext.containerView// come from viewController (来源页面的)// viewControllerlet fromVC = transitionContext.viewController(forKey: .from)// viewlet fromView = transitionContext.view(forKey: .from)// 初始framelet fromViewInitialFrame = transitionContext.initialFrame(for: fromVC)// 最终framelet fromViewFinalFrame = transitionContext.finalFrame(for: fromVC)// to viewController (跳转页面的)// viewControllerlet toVC = transitionContext.viewController(forKey: .to)// viewlet toView = transitionContext.view(forKey: .to)// 初始framevar toViewInitialFrame = transitionContext.initialFrame(for: toVC)// 最终framelet 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 viewtoView.frame = toViewFinalFrame// Set additional views final values......} completion: { finish in// 5.2 get animation resultlet success = !transitionContext.transitionWasCancelled// 5.2.1 remove the view if animation failif !success {toView.removeFromSuperview()}// 5.2.1 callback animation resulttransitionContext.completeTransition(success)}
}

3.3 动画结束

动画结束方法:

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,即还停留在原来的页面上。
/// 转场动画
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 viewfromView.frame = fromViewFinalFrame} completion: { finish in// 5.2 get animation resultlet success = !transitionContext.transitionWasCancelled// 5.2.1 remove the view after animation finishif success {fromView.removeFromSuperview()}transitionContext.completeTransition(success)}
}

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


4、UIPresentationController

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

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

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

效果如如下:
presentController


4.1 设置presentVC的frame

// presentVC 在动画容器上的 frame
override var frameOfPresentedViewInContainerView: CGRect {get {                    let containerBounds: CGRect = self.containerView?.bounds ?? .zerolet width = CGFloat(floorf(Float(containerBounds.size.width) / 2.0))let height = containerBounds.size.heightlet originX = containerBounds.size.width - widthreturn CGRect(x: originX, y: 0.0, width: width, height: height)}
}

4.2 present 动画

// 暗色背景
lazy var moDimmingView: UIView = {let view = UIView(frame: .zero)view.backgroundColor = UIColor(white: 0.0, alpha: 0.4)view.alpha = 0.0return view
}()
// 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.boundsself.moDimmingView.alpha = 0.0containerView.insertSubview(self.moDimmingView, at: 0)// 3. execute animation (执行动画)// 这里尝试去拿一个时间点的回调,能拿到就在回调里执行显示动画;拿不到就直接设置显示guard let transitionCoordinator = self.presentedViewController.transitionCoordinator else {self.moDimmingView.alpha = 1.0return}transitionCoordinator.animateAlongsideTransition(in: self.presentedView) { context inself.moDimmingView.alpha = 1.0}
}// MARK: - present 动画结束
override func presentationTransitionDidEnd(_ completed: Bool) {super.presentationTransitionDidEnd(completed)    // remove dark background view when transition failif !completed {self.moDimmingView.removeFromSuperview()}
}

4.3 dismiss 动画

// MARK: - 将要开始 dismiss,设置初始值 和 动画回调
override func dismissalTransitionWillBegin() {super.dismissalTransitionWillBegin()    guard let transitionCoordinator = self.presentedViewController.transitionCoordinator else {self.moDimmingView.alpha = 0.0return}transitionCoordinator.animateAlongsideTransition(in: self.presentedView) { context inself.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

相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
有效的括号 一、题目 给定一个只包括 '(',')','{','}'...
【Ctfer训练计划】——(三... 作者名:Demo不是emo  主页面链接:主页传送门 创作初心ÿ...