本文转载至 http://www.cocoachina.com/ios/20151223/14739.htmlhtml
感谢原创做者分享ios
前言
相信不少人对实现 iOS 中的动画效果都特别头疼,每每懒得动手,功能实现不就得了,何须要那么花哨、装13的东西。可是看到别人的炫酷动效,心中又瘙痒不已,便下定决心学习,因而开始翻看 Core Animation、UIView动画(实际上是对Core Animation的一种封装)相关资料。不当心看到一群大神正在热烈讨论,钻一进去一看,原来是 POP(潜意识:Facebook出品必属精品),这还学什么Core Animation,果断pod一个来玩玩,因而你就左手CA,右手 POP 开森地把玩起来了。git
此时,你可能已经学会了CA的基本使用方法,也对UIView动画的便捷感到惊喜,可是不知足的你,显然有更高的追求,POP 以其灵活的用法,丰富的动效,完整的API文档,深得不少程序员的喜好。做为一个有逼格的程序员,这么流行的框架,必然是值得深刻学习的,可是你是否考虑过这样的第三方动画框架是否存在什么不足。所以,做为一个有追求的程序员,有必要来稍微深刻地探讨一下 Core Animation 和 POP 不一样点。程序员
Core Animation 工做机制github
首先咱们须要了解CA是如何工做的。每当咱们建立并添加动画到 layer 时,QuartzCore 框架就会把动画的参数打包好,而后经过 IPC (处理器)发送给名为 backboardd 的后台处理程序。你的应用也会发送当前展现在屏幕上的每个 layer 的信息。app
backboardd 会处理 layer 的结构体系而后经过 OpenGL 绘制出来。它还会处理你已经添加过的动画(也能够是视图,由于视图本质是包裹着 layer的)。你必定要理解的是,backboardd 使得动画的每一帧均可以在你的应用中彻底独立。这里惟一的回调是动画的开始和结束(详见CAAnimationDelegate 协议)。你的应用彻底不会参与动画的绘制,这些绘制彻底独立于你的应用进程(除非你明确地在你的应用中经过动画通用属性要求绘制动画帧)。这意味着你能够继续在主线程作其余事情,而且不会影响到 CAAnimation 的性能。若是你阻塞了你的主线程,或者你在调试器中暂停了你的程序,你的动画仍是会继续执行。框架
可是你可能会有这样的疑问:每一个 CALayer 不是还有一个 presentationLayer 属性吗?iphone
presentationLayer的官方解释:性能
“While an animation is in progress, you can retrieve this object and use it to get the current values for those animations.”学习
当CAAnimation发生时,你在屏幕上看到的其实是 presentation layer 的改变。若是你访问 presentation layer,QuartzCore 将会计算现有的帧状态,而且使用这个帧状态去构建 presentation layer 对象。由于动画状态在动画执行期间一直处于改变,所以你将会得到近似值。
POP 工做机制
如今有不少优秀的第三方动画库,POP 由于其使用灵活、功能强大、文档齐全,因此备受好评,先看一下官方介绍:
POP是一个在iOS与OS X上通用的极具扩展性的动画引擎 它在基本的静态动画的基础上增长的弹簧动画与衰减动画。
使之能创造出更真实更具物理性的交互动画 POP的API能够快速的与现有的ObjC代码集成并能够做用于任意对象的任意属性。
POP是个至关成熟且久经考验的框架 Facebook出品的使人惊叹的Paper应用中的全部动画和效果即出自POP。
更为详细的介绍和使用请查看官方文档以及里脊串的 POP介绍与使用实践(快速上手动画)。
POP 本质上是基于定时器的动画库,使用每秒 60 频率的定时器,即时钟频率为 1/60 秒(为了匹配 iOS 显示屏帧率),使得动画刷新绘制频率与屏幕刷新频率一致。不少这类动画库都使用 CADisplayLink 作为一个回调源。
一旦定时器刷新,动画库计算动画的进程,这意味着动画库会计算那些活动的东西的状态(一般是layer 属性,如 bound,opactiy,transform 等)。而后动画库提供最新计算的值给有动画的 layer (或者其余对象)。最主要的区别是,layer 的状态将会在这种状况下改变。
因为 layer 的一些参数已经被改变,你的应用必须经过 IPC 通知 backboardd 处理这些变化。当 backboardd 接收到变化通知(同时接收到的还有应用中的 layer 树),它将在屏幕上重绘一切东西。这意味着,你应用中作的每个动画帧都会传送数据到 backboardd (即通知 backboardd ),由于 backboardd 彻底不知道 layer 发生了什么事情。综上,你的应用就是在这种状况下运行动画的。
Core Animation 和 POP 运行动画对比
因为 POP 是基于定时器定时刷新添加动画的原理,那么若是将动画库运行在主线程上,会因为线程阻塞的问题致使动画效果出现卡顿、不流畅的状况。更为关键的是,你不能将动画效果放在子线程,由于你不能将对 view 和 layer 的操做放到主线程以外。
为了验证上述的观点,我作了一个实验,首先用CA动画制做一个能够旋转的 view:
1
2
3
4
5
6
7
8
|
UIView *viewCA = [[UIView alloc]initWithFrame:CGRectMake(50,50, 100, 100)];
viewCA.backgroundColor = [UIColor blueColor];
[self.view addSubview:viewCA];
CABasicAnimation *caAnimation = [CABasicAnimation animationWithKeyPath:@
"transform.rotation.z"
];
caAnimation.toValue = @(M_PI);
caAnimation.duration = 2.0;
caAnimation.repeatCount = 500;
[viewCA.layer addAnimation:caAnimation forKey:@
"anim"
];
|
再建立一个利用 POP 动画库制做的可旋转 view:
1
2
3
4
5
6
7
8
9
|
UIView *viewPOP = [[UIView alloc]initWithFrame:
CGRectMake(CGRectGetWidth(self.view.bounds) - 100 - 50, 50, 100, 100)];
viewPOP.backgroundColor = [UIColor yellowColor];
[self.view addSubview:viewPOP];
POPBasicAnimation *popAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerRotation];
popAnimation.toValue = @(M_PI);
popAnimation.duration = 2.0;
popAnimation.repeatCount = 500;
[viewPOP.layer pop_addAnimation:popAnimation forKey:@
"rotation"
];
|
在没有线程阻塞的状况下,对比两个动画库的运行效果以下:
能够看出来虽然在没有线程阻塞,可是 POP 的动画在结束时有一个明显的中止动做,是由于 POP 的动画效果很差吗?
答案是 timingFunction
CoreAnimation 和 POPBasicAnimation提供一样的五种 timingFunction:
kCAMediaTimingFunctionLinear
kCAMediaTimingFunctionEaseIn
kCAMediaTimingFunctionEaseOut
kCAMediaTimingFunctionEaseInEaseOut
kCAMediaTimingFunctionDefault
重点说一下:kCAMediaTimingFunctionDefault(引自:iOS-Core-Animation-Advanced-Techniques(五))
它和kCAMediaTimingFunctionEaseInEaseOut很相似,可是加速和减速的过程都稍微有些慢。它和kCAMediaTimingFunctionEaseInEaseOut的区别很难察觉,多是苹果以为它对于隐式动画来讲更适合(而后对UIKit就改变了想法,而是使用kCAMediaTimingFunctionEaseInEaseOut做为默认效果),虽然它的名字说是默认的,但仍是要记住当建立显式的CAAnimation它并非默认选项(换句话说,默认的图层行为动画用kCAMediaTimingFunctionDefault做为它们的计时方法)。
若是不设置 timingFunction 属性,那么在使用 CA 的状况下, timingFunction 是kCAMediaTimingFunctionLinear 的,而 POP 倒是kCAMediaTimingFunctionEaseOut ,所以咱们只要添加这么一行代码:
1
|
popAnimation.timingFunction?=?[CAMediaTimingFunction?functionWithName:kCAMediaTimingFunctionLinear];
|
如今再看效果:
能够看出来,在主线程没有阻塞的状况下,两种动画库的表现并没有差别。
如今咱们来制造一点难度,人工利用线程的 sleep 增长一个主线程阻塞:
1
2
3
4
5
6
|
- (void)repeatedlyBlockMainThread
{
NSLog(@
"blocking main thread!"
);
[NSThread sleepForTimeInterval:0.25];
[self performSelector:@selector(repeatedlyBlockMainThread) withObject:nil afterDelay:1];
}
|
而后再 viewDidLoad 里面调用 :
1
|
[self performSelector:@selector(repeatedlyBlockMainThread) withObject:nil afterDelay:1];
|
如今再来看一下二者的动画效果:
很明显,咱们能够看出来,因为添加了主线程阻塞,利用 POP 制做的动画视图,在每隔 1s 都会卡顿一下,而 CA 的视图却彻底不受主线程阻塞的影响。
总结
经过此次简单的对比,咱们从工做机制上了解了 CA 和 POP 两个动画库的基本原理,并用简单的动画效果对比,重现了在主线程阻塞的状况下二者的差别,很显然, POP 受主线程阻塞的影响很大,在使用过程当中,应避免在有可能发生主线程阻塞的状况下使用 POP ,避免制做卡顿的动画效果,产生很差的用户体验。文中提出了 POP 的这种缺点,可是 POP 毕竟是久经考验的动画技术,本人也正在学习中,有错误的地方吝请指正。
对比系列,是我的比较喜欢的一种学习方式,经过对比,找出不一样技术的优缺点,能够更合理地使用这些武器,俗话说:好钢用在刀刃上,大抵如此。