




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、自定义转场动画-Swift3.0版本转场动画这事,说简单也简单,可以通过presentViewController:animated:completion:和dismissViewControllerAnimated:completion:这一组函数以模态视图的方式展现、隐藏视图。如果用到了navigationController,还可以调用pushViewController:animated:和popViewController这一组函数将新的视图控制器压栈、弹栈。下图中所有转场动画都是自定义的动画,这些效果如果不用自定义动画则很难甚至无法实现:demo演示由于录屏的原因,有些效果无法完全
2、展现,比如它其实还支持横屏。自定义转场动画的效果实现起来比较复杂,如果仅仅是拷贝一份能够运行的代码却不懂其中原理,就有可能带来各种隐藏的bug。本文由浅入深介绍下面几个知识:传统的基于闭包的实现方式及其缺点自定义present转场动画交互式(Interactive)转场动画转场协调器与UIModalPresentationCustomUINavigationController转场动画我为这篇教程制作了一个demo,您可以去在我的github上clone下来:CustomTransition,如果觉得有帮助还望给个star以示支持。本文以Swift+纯代码实现,对应的OC+Storyboard
3、版本在demo中也可以找到,那是苹果的官方示范代码,正确性更有保证。demo中用到了CocoaPods,您也许需要执行pod install命令并打开.xcworkspace文件。在开始正式的教程前,您首先需要下载demo,在代码面前文字是苍白的,demo中包含的注释足以解释本文所有的知识点。其次,您还得了解这几个背景知识。From和To在代码和文字中,经常会出现fromView和toView。如果错误的理解它们的含义会导致动画逻辑完全错误。fromView表示当前视图,toView表示要跳转到的视图。如果是从A视图控制器present到B,则A是from,B是to。从B视图控制器dismis
4、s到A时,B变成了from,A是to。用一张图表示:from和toPresented和Presenting这也是一组相对的概念,它容易与fromView和toView混淆。简单来说,它不受present或dismiss的影响,如果是从A视图控制器present到B,那么A总是B的presentingViewController,B总是A的presentedViewController。modalPresentationStyle这是一个枚举类型,表示present时动画的类型。其中可以自定义动画效果的只有两种:FullScreen和Custom,两者的区别在于FullScreen会移除from
5、View,而Custom不会。比如文章开头的gif中,第三个动画效果就是Custom。基于block的动画最简单的转场动画是使用transitionFromViewController方法:传统的转场动画实现这个方法虽然已经过时,但是对它的分析有助于后面知识的理解。它一共有6个参数,前两个表示从哪个VC开始,跳转到哪个VC,中间两个参数表示动画的时间和选项。最后两个参数表示动画的具体实现细节和回调闭包。这六个参数其实就是一次转场动画所必备的六个元素。它们可以分为两组,前两个参数为一组,表示页面的跳转关系,后面四个为一组,表示动画的执行逻辑。这个方法的缺点之一是可自定义程度不高(在后面您会发现能
6、自定义的不仅仅是动画方式),另一个缺点则是重用性不好,也可以说是耦合度比较大。在最后两个闭包参数中,可以预见的是fromViewController和toViewController参数都会被用到,而且他们是动画的关键。假设视图控制器A可以跳转到B、C、D、E、F,而且跳转动画基本相似,您会发现transitionFromViewController方法要被复制多次,每次只会修改少量内容。自定义present转场动画出于解耦和提高可自定义程度的考虑,我们来学习转场动画的正确使用姿势。首先要了解一个关键概念:转场动画代理,它是一个实现了UIViewControllerTransitioningD
7、elegate协议的对象。我们需要自己实现这个对象,它的作用是为UIKit提供以下几个对象中的一个或多个:Animator:它是实现了UIViewControllerAnimatedTransitioning协议的对象,用于控制动画的持续时间和动画展示逻辑,代理可以为present和dismiss过程分别提供Animator,也可以提供同一个Animator。交互式Animator:和Animator类似,不过它是交互式的,后面会有详细介绍Presentation控制器:它可以对present过程更加彻底的自定义,比如修改被展示视图的大小,新增自定义视图等,后面会有详细介绍。转场动画代理在这一
8、小节中,我们首先介绍最简单的Animator。回顾一下转场动画必备的6个元素,它们被分为两组,彼此之间没有关联。Animator的作用等同于第二组的四个元素,也就是说对于同一个Animator,可以适用于A跳转B,也可以适用于A跳转C。它表示一种通用的页面跳转时的动画逻辑,不受限于具体的视图控制器。如果您读懂了这段话,整个自定义的转场动画逻辑就很清楚了,以视图控制器A跳转到B为例:创建动画代理,在事情比较简单时,A自己就可以作为代理设置B的transitioningDelegate为步骤1中创建的代理对象调用presentViewController:animated:completion:并
9、把参数animated设置为true系统会找到代理中提供的Animator,由Animator负责动画逻辑用具体的例子解释就是:/ 这个类相当于Aclass CrossDissolveFirstViewController: UIViewController, UIViewControllerTransitioningDelegate / 这个对象相当于B crossDissolveSecondViewController.transitioningDelegate = self / 点击按钮触发的函数 func animationButtonDidClicked() self.present
10、ViewController(crossDissolveSecondViewController, animated: true, completion: nil) / 下面这两个函数定义在UIViewControllerTransitioningDelegate协议中 / 用于为present和dismiss提供animator func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, source
11、Controller source: UIViewController) - UIViewControllerAnimatedTransitioning? / 也可以使用CrossDissolveAnimator,动画效果各有不同/ return CrossDissolveAnimator() return HalfWaySpringAnimator() func animationControllerForDismissedController(dismissed: UIViewController) - UIViewControllerAnimatedTransitioning? retu
12、rn CrossDissolveAnimator() 动画的关键在于animator如何实现,它实现了UIViewControllerAnimatedTransitioning协议,至少需要实现两个方法,我建议您仔细阅读animateTransition方法中的注释,它是整个动画逻辑的核心:class HalfWaySpringAnimator: NSObject, UIViewControllerAnimatedTransitioning / 设置动画的持续时间 func transitionDuration(transitionContext: UIViewControllerContex
13、tTransitioning?) - NSTimeInterval return 2 / 设置动画的进行方式,附有详细注释,demo中其他地方的这个方法不再解释 func animateTransition(transitionContext: UIViewControllerContextTransitioning) let fromViewController = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey) let toViewController = transition
14、Context.viewControllerForKey(UITransitionContextToViewControllerKey) let containerView = transitionContext.containerView() / 需要关注一下from/to和presented/presenting的关系 / For a Presentation: / fromView = The presenting view. / toView = The presented view. / For a Dismissal: / fromView = The presented view
15、. / toView = The presenting view. var fromView = fromViewController?.view var toView = toViewController?.view / iOS8引入了viewForKey方法,尽可能使用这个方法而不是直接访问controller的view属性 / 比如在form sheet样式中,我们为presentedViewController的view添加阴影或其他decoration,animator会对整个decoration view / 添加动画效果,而此时presentedViewController的vi
16、ew只是decoration view的一个子视图 if transitionContext.respondsToSelector(Selector(viewForKey:) fromView = transitionContext.viewForKey(UITransitionContextFromViewKey) toView = transitionContext.viewForKey(UITransitionContextToViewKey) / 我们让toview的origin.y在屏幕的一半处,这样它从屏幕的中间位置弹起而不是从屏幕底部弹起,弹起过程中逐渐变为不透明 toView?
17、.frame = CGRectMake(fromView!.frame.origin.x, fromView!.frame.maxY / 2, fromView!.frame.width, fromView!.frame.height) toView?.alpha = 0.0 / 在present和,dismiss时,必须将toview添加到视图层次中 containerView?.addSubview(toView!) let transitionDuration = self.transitionDuration(transitionContext) / 使用spring动画,有弹簧效果,
18、动画结束后一定要调用completeTransition方法 UIView.animateWithDuration(transitionDuration, delay: 0, usingSpringWithDamping: 0.6, initialSpringVelocity: 0, options: .CurveLinear, animations: () - Void in toView!.alpha = 1.0 / 逐渐变为不透明 toView?.frame = transitionContext.finalFrameForViewController(toViewController!
19、) / 移动到指定位置 ) (finished: Bool) - Void in let wasCancelled = transitionContext.transitionWasCancelled() transitionCpleteTransition(!wasCancelled) animateTransition方法的核心则是从转场动画上下文获取必要的信息以完成动画。上下文是一个实现了UIViewControllerContextTransitioning的对象,它的作用在于为animateTransition方法提供必备的信息。您不应该缓存任何关于动画的信息,而是应该总是从转场动画
20、上下文中获取(比如fromView和toView),这样可以保证总是获取到最新的、正确的信息。转场动画上下文获取到足够信息后,我们调用UIView.animateWithDuration方法把动画交给Core Animation处理。千万不要忘记在动画调用结束后,执行completeTransition方法。本节的知识在Demo的Cross Dissolve文件夹中有详细的代码。其中有两个animator文件,这说明我们可以为present和dismiss提供同一个animator,或者分别提供各自对应的animator。如果两者动画效果类似,您可以共用同一个animator,惟一的区别在于:
21、present时,要把toView加入到container的视图层级。dismiss时,要把fromView从container的视图层级中移除。如果您被前面这一大段代码和知识弄晕了,或者暂时用不到这些具体的知识,您至少需要记住自定义动画的基本原理和流程:设置将要跳转到的视图控制器(presentedViewController)的transitioningDelegate充当代理的对象可以是源视图控制器(presentingViewController),也可以是自己创建的对象,它需要为转场动画提供一个animator对象。animator对象的animateTransition是整个动画的
22、核心逻辑。交互式(Interactive)转场动画刚刚我们说到,设置了toViewController的transitioningDelegate属性并且present时,UIKit会从代理处获取animator,其实这里还有一个细节:UIKit还会调用代理的interactionControllerForPresentation:方法来获取交互式控制器,如果得到了nil则执行非交互式动画,这就回到了上一节的内容。如果获取到了不是nil的对象,那么UIKit不会调用animator的animateTransition方法,而是调用交互式控制器(还记得前面介绍动画代理的示意图么,交互式动画控制器
23、和animator是平级关系)的startInteractiveTransition:方法。所谓的交互式动画,通常是基于手势驱动,产生一个动画完成的百分比来控制动画效果(文章开头的gif中第二个动画效果)。整个动画不再是一次性、连贯的完成,而是在任何时候都可以改变百分比甚至取消。这需要一个实现了UIPercentDrivenInteractiveTransition协议的交互式动画控制器和animator协同工作。这看上去是一个非常复杂的任务,但UIKit已经封装了足够多细节,我们只需要在交互式动画控制器和中定义一个时间处理函数(比如处理滑动手势),然后在接收到新的事件时,计算动画完成的百分比
24、并且调用updateInteractiveTransition来更新动画进度即可。用下面这段代码简单表示一下整个流程(删除了部分细节和注释,请不要以此为正确参考),完整的代码请参考demo中的Interactivity文件夹:/ 这个相当于fromViewControllerclass InteractivityFirstViewController: UIViewController / 这个相当于toViewController lazy var interactivitySecondViewController: InteractivitySecondViewController = I
25、nteractivitySecondViewController() / 定义了一个InteractivityTransitionDelegate类作为代理 lazy var customTransitionDelegate: InteractivityTransitionDelegate = InteractivityTransitionDelegate() override func viewDidLoad() super.viewDidLoad() setupView() / 主要是一些UI控件的布局,可以无视其实现细节 / 设置动画代理,这个代理比较复杂,所以我们新建了一个代理对象而不
26、是让self作为代理 interactivitySecondViewController.transitioningDelegate = customTransitionDelegate / 触发手势时,也会调用animationButtonDidClicked方法 func interactiveTransitionRecognizerAction(sender: UIScreenEdgePanGestureRecognizer) if sender.state = .Bgan self.animationButtonDidClicked(sender) func animationButt
27、onDidClicked(sender: AnyObject) self.presentViewController(interactivitySecondViewController, animated: true, completion: nil) 非交互式的动画代理只需要为present和dismiss提供animator即可,但是在交互式的动画代理中,还需要为present和dismiss提供交互式动画控制器:class InteractivityTransitionDelegate: NSObject, UIViewControllerTransitioningDelegate fu
28、nc animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) - UIViewControllerAnimatedTransitioning? return InteractivityTransitionAnimator(targetEdge: targetEdge) func animationControllerForD
29、ismissedController(dismissed: UIViewController) - UIViewControllerAnimatedTransitioning? return InteractivityTransitionAnimator(targetEdge: targetEdge) / 前两个函数和淡入淡出demo中的实现一致 / 后两个函数用于实现交互式动画 func interactionControllerForPresentation(animator: UIViewControllerAnimatedTransitioning) - UIViewControlle
30、rInteractiveTransitioning? return TransitionInteractionController(gestureRecognizer: gestureRecognizer, edgeForDragging: targetEdge) func interactionControllerForDismissal(animator: UIViewControllerAnimatedTransitioning) - UIViewControllerInteractiveTransitioning? return TransitionInteractionControl
31、ler(gestureRecognizer: gestureRecognizer, edgeForDragging: targetEdge) animator中的代码略去,它和非交互式动画中的animator类似。因为交互式的动画只是一种锦上添花,它必须支持非交互式的动画,比如这个例子中,点击按钮依然出发的是非交互式的动画,只是手势滑动才会触发交互式动画。class TransitionInteractionController: UIPercentDrivenInteractiveTransition / 当手势有滑动时触发这个函数 func gestureRecognizeDidUpdat
32、e(gestureRecognizer: UIScreenEdgePanGestureRecognizer) switch gestureRecognizer.state case .Began: break case .Changed: self.updateInteractiveTransition(self.percentForGesture(gestureRecognizer) /手势滑动,更新百分比 case .Ended: / 滑动结束,判断是否超过一半,如果是则完成剩下的动画,否则取消动画 if self.percentForGesture(gestureRecognizer)
33、= 0.5 self.finishInteractiveTransition() else self.cancelInteractiveTransition() default: self.cancelInteractiveTransition() private func percentForGesture(gesture: UIScreenEdgePanGestureRecognizer) - CGFloat let percent = 根据gesture计算得出 return percent 交互式动画是在非交互式动画的基础上实现的,我们需要创建一个继承自UIPercentDrivenI
34、nteractiveTransition类型的子类,并且在动画代理中返回这个类型的实例对象。在这个类型中,监听手势(或者下载进度等等)的时间变化,然后调用percentForGesture方法更新动画进度即可。转场协调器与UIModalPresentationCustom在进行转场动画的同时,您还可以进行一些同步的,额外的动画,比如文章开头gif中的第三个例子。presentedView和presentingView可以更改自身的视图层级,添加额外的效果(阴影,圆角)。UIKit使用转成协调器来管理这些额外的动画。您可以通过需要产生动画效果的视图控制器的transitionCoordinato
35、r属性来获取转场协调器,转场协调器只在转场动画的执行过程中存在。转场动画协调器想要完成gif中第三个例子的效果,我们还需要使用UIModalPresentationStyle.Custom来代替.FullScreen。因为后者会移除fromViewController,这显然不符合需求。当present的方式为.Custom时,我们还可以使用UIPresentationController更加彻底的控制转场动画的效果。一个 presentation controller具备以下几个功能:设置presentedViewController的视图大小添加自定义视图来改变presentedView的
36、外观为任何自定义的视图提供转场动画效果根据size class进行响应式布局您可以认为,. FullScreen以及其他present风格都是swift为我们实现提供好的,它们是.Custom的特例。而.Custom允许我们更加自由的定义转场动画效果。UIPresentationController提供了四个函数来定义present和dismiss动画开始前后的操作:presentationTransitionWillBegin: present将要执行时presentationTransitionDidEnd:present执行结束后dismissalTransitionWillBegin:
37、dismiss将要执行时dismissalTransitionDidEnd:dismiss执行结束后下面的代码简要描述了gif中第三个动画效果的实现原理,您可以在demo的Custom Presentation文件夹下查看完成代码:/ 这个相当于fromViewControllerclass CustomPresentationFirstViewController: UIViewController / 这个相当于toViewController lazy var customPresentationSecondViewController: CustomPresentationSecond
38、ViewController = CustomPresentationSecondViewController() / 创建PresentationController lazy var customPresentationController: CustomPresentationController = CustomPresentationController(presentedViewController: self.customPresentationSecondViewController, presentingViewController: self) override func
39、viewDidLoad() super.viewDidLoad() setupView() / 主要是一些UI控件的布局,可以无视其实现细节 / 设置转场动画代理 customPresentationSecondViewController.transitioningDelegate = customPresentationController override func didReceiveMemoryWarning() super.didReceiveMemoryWarning() / Dispose of any resources that can be recreated. func
40、 animationButtonDidClicked() self.presentViewController(customPresentationSecondViewController, animated: true, completion: nil) 重点在于如何实现CustomPresentationController这个类:class CustomPresentationController: UIPresentationController, UIViewControllerTransitioningDelegate var presentationWrappingView: U
41、IView? / 这个视图封装了原视图,添加了阴影和圆角效果 var dimmingView: UIView? = nil / alpha为0.5的黑色蒙版 / 告诉UIKit为哪个视图添加动画效果 override func presentedView() - UIView? return self.presentationWrappingView / 四个方法自定义转场动画发生前后的操作extension CustomPresentationController override func presentationTransitionWillBegin() / 设置presentation
42、WrappingView和dimmingView的UI效果 let transitionCoordinator = self.presentingViewController.transitionCoordinator() self.dimmingView?.alpha = 0 / 通过转场协调器执行同步的动画效果 transitionCoordinator?.animateAlongsideTransition( (context: UIViewControllerTransitionCoordinatorContext) - Void in self.dimmingView?.alpha
43、= 0.5 , completion: nil) / present结束时,把dimmingView和wrappingView都清空,这些临时视图用不到了 override func presentationTransitionDidEnd(completed: Bool) if !completed self.presentationWrappingView = nil self.dimmingView = nil / dismiss开始时,让dimmingView完全透明,这个动画和animator中的动画同时发生 override func dismissalTransitionWill
44、Begin() let transitionCoordinator = self.presentingViewController.transitionCoordinator() transitionCoordinator?.animateAlongsideTransition( (context: UIViewControllerTransitionCoordinatorContext) - Void in self.dimmingView?.alpha = 0 , completion: nil) / dismiss结束时,把dimmingView和wrappingView都清空,这些临时视图用不到了 override func dismissalTransitionDidEnd(completed: Bool) if ted self.presentationWrappingView = nil self.dimmingView = nil extension CustomPr
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 买卖合同房屋买卖协议
- 小区绿化环保工程施工协议
- 2025短期用工合同范本
- 2025项目经理劳动合同劳动合同范本
- 现代管理学重要题型试题及答案
- 2025计算机设备采购合同范本 计算机设备采购合同(年度)
- 2025竹林经营合同
- 行政预算与控制分析试题及答案
- 2025建筑工程监理合同范本
- 公文处理中的文化适宜性分析试题及答案
- 思政课社会实践报告1500字6篇
- 常暗之厢(7规则-简体修正)
- GB∕T 25119-2021 轨道交通 机车车辆电子装置
- 电池PCBA规格书
- 机械零件加工验收检验记录(共2页)
- 机械加工切削全参数推荐表
- 终端塔基础预偏值(抬高值)计算表格
- 海外医疗服务委托合同协议书范本模板
- (完整版)研究者手册模板
- 菲林检验及管理办法
- 磁芯参数对照表
评论
0/150
提交评论