iOS动画学习 -隐式动画
事务
Core Animation基于一个假设,说屏幕上的任何东西都可以(或者可能)做动画。你并不需要在Core Animation中手动打开动画,但是你需要明确地关闭它,否则它会一直存在。
当你改变CALayer一个可做动画的属性时,这个改变并不会立刻在屏幕上体现出来。相反,该属性会从先前的值平滑过渡到新的值。这一切都是默认的行为,你不需要做额外的操作。
接下来看一个例子,老样子,先上代码
@interface ViewController ()
@property (nonatomic, strong) CALayer *colorLayer;
@end @implementation ViewController - (void)viewDidLoad {
[super viewDidLoad];
// 创建colorLayer,注意一定要另外创建一个CALayer对象,不要用与视图关联的layer,原因我们后续会讲到
self.colorLayer = [CALayer layer];
self.colorLayer.frame = CGRectMake(50.0f, 50.0f, 100.0f, 100.0f);
self.colorLayer.backgroundColor = [UIColor blueColor].CGColor; [self.view.layer addSublayer:self.colorLayer]; }
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
CGFloat red = arc4random() / (CGFloat)INT_MAX;
CGFloat green = arc4random() / (CGFloat)INT_MAX;
CGFloat blue = arc4random() / (CGFloat)INT_MAX;
self.colorLayer.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0].CGColor;
}
@end
运行之后的初始状态

这里为了方便,我们将改变layer背景色的代码放在了touchesBegan中,点击视图,我们可以看到layer的背景色缓慢的变化为一个新的颜色(系统默认的动画周期是0.25秒,可能不太明显),像这种不显示创建动画对象的方式,称之为隐式动画。
但当你改变一个属性,Core Animation是如何判断动画类型和持续时间的呢?实际上动画执行的时间取决于当前事务的设置,动画类型取决于图层行为。
事务实际上是Core Animation用来包含一系列属性动画集合的机制,任何用指定事务去改变可以做动画的图层属性都不会立刻发生变化,而是当事务一旦提交的时候开始用一个动画过渡到新值。
事务是通过CATransaction类来做管理,这个类的设计有些奇怪,不像你从它的命名预期的那样去管理一个简单的事务,而是管理了一叠你不能访问的事务。CATransaction没有属性或者实例方法,并且也不能用+alloc和-init方法创建它。而是用类方法+begin和+commit分别来入栈或者出栈。任何可以做动画的图层属性都可以添加到栈顶的事务。
接下来我们对之前的代码进行更改,用CATransaction进行一些控制
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[CATransaction begin];
[CATransaction setAnimationDuration:1.0];
CGFloat red = arc4random() / (CGFloat)INT_MAX;
CGFloat green = arc4random() / (CGFloat)INT_MAX;
CGFloat blue = arc4random() / (CGFloat)INT_MAX;
self.colorLayer.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0].CGColor;
[CATransaction commit];
}
我们通过CATransaction对动画周期做了设置,可以明显看出动画过程
完成块
CATransaction中有一个完成动画后的回调,我们添加上可以看看
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[CATransaction begin];
[CATransaction setAnimationDuration:1.0];
[CATransaction setCompletionBlock:^{
CGAffineTransform transform = self.colorLayer.affineTransform;
transform = CGAffineTransformRotate(transform, M_PI_4);
self.colorLayer.affineTransform = transform;
}];
CGFloat red = arc4random() / (CGFloat)INT_MAX;
CGFloat green = arc4random() / (CGFloat)INT_MAX;
CGFloat blue = arc4random() / (CGFloat)INT_MAX;
self.colorLayer.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0].CGColor;
[CATransaction commit];
}
运行后点击视图,可以看到如下图

注意旋转动画要比颜色渐变快得多,这是因为完成块是在颜色渐变的事务提交并出栈之后才被执行,于是,用默认的事务做变换,默认的时间也就变成了0.25秒。
图层行为
接下来就到了解释为什么不要直接操作与视图关联的图层,这里我们先添加一个按钮和一个视图,添加完之后这个样子

点击按钮时,要响应如下方法
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIView *animationView;
@end - (IBAction)changeColor:(UIButton *)sender {
[CATransaction begin];
[CATransaction setAnimationDuration:1.0]; CGFloat red = arc4random() / (CGFloat)INT_MAX;
CGFloat green = arc4random() / (CGFloat)INT_MAX;
CGFloat blue = arc4random() / (CGFloat)INT_MAX;
self.animationView.layer.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0].CGColor; [CATransaction commit]; }
当点击按钮时,我们并没有看到视图的背景慢慢过渡到新颜色,而是立刻变化的,这是为什么呢?
试想一下,如果UIView的属性都有动画特性的话,那么无论在什么时候修改它,我们都应该能注意到的。所以,如果说UIKit建立在Core Animation(默认对所有东西都做动画)之上,那么隐式动画是如何被UIKit禁用掉呢?
我们把改变属性时CALayer自动应用的动画称作行为,当CALayer的属性被修改时候,它会调用-actionForKey:方法,传递属性的名称。剩下的操作都在CALayer的头文件中有详细的说明,实质上是如下几步:
- 图层首先检测它是否有委托,并且是否实现
CALayerDelegate协议指定的-actionForLayer:forKey方法。如果有,直接调用并返回结果。 - 如果没有委托,或者委托没有实现
-actionForLayer:forKey方法,图层接着检查包含属性名称对应行为映射的actions字典。 - 如果
actions字典没有包含对应的属性,那么图层接着在它的style字典接着搜索属性名。 - 最后,如果在
style里面也找不到对应的行为,那么图层将会直接调用定义了每个属性的标准行为的-defaultActionForKey:方法。
所以一轮完整的搜索结束之后,-actionForKey:要么返回空(这种情况下将不会有动画发生),要么是CAAction协议对应的对象,最后CALayer拿这个结果去对先前和当前的值做动画。
于是这就解释了UIKit是如何禁用隐式动画的:每个UIView对它关联的图层都扮演了一个委托,并且提供了-actionForLayer:forKey的实现方法。当不在一个动画块的实现中,UIView对所有图层行为返回nil,但是在动画block范围之内,它就返回了一个非空值。我们可以用一个demo做个简单的实验
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"Outside: %@", [self.animationView actionForLayer:self.animationView.layer forKey:@"backgroundColor"]);
[UIView beginAnimations:nil context:nil];
NSLog(@"Inside: %@", [self.animationView actionForLayer:self.animationView.layer forKey:@"backgroundColor"]);
[UIView commitAnimations];
// Do any additional setup after loading the view, typically from a nib.
}
控制台打印结果
-- ::05.397157+ Animation[:] Outside: <null>
-- ::05.398090+ Animation[:] Inside: <CABasicAnimation: 0x6040004362a0>
所以跟猜测的一样,当属性在动画块之外发生改变,UIView直接通过返回nil来禁用隐式动画。但如果在动画块范围之内,根据动画具体类型返回相应的属性,在这个例子就是CABasicAnimation
当然返回nil并不是禁用隐式动画唯一的办法,CATransaction有个方法叫做+setDisableActions:,可以用来对所有属性打开或者关闭隐式动画。
总结
这一章讨论了隐式动画,还有Core Animation对指定属性选择合适的动画行为的机制。同时你知道了UIKit是如何充分利用Core Animation的隐式动画机制来强化它的显式系统,以及动画是如何被默认禁用并且当需要的时候启用的。
iOS动画学习 -隐式动画的更多相关文章
- iOS:CALayer的隐式动画的详解
CALayer的隐式动画属性: •每一个UIView内部都默认关联着一个CALayer,称这个Layer为Root Layer.所有的非Root Layer都存在着隐式动画,隐式动画的默认时长为1/4 ...
- iOS中的隐式动画
隐式动画就是指 在 非 人为在代码中 定义动画 而系统却默认 自带 的动画 叫做隐式动画. 比如 改变 图层 的颜色 位置 和 透明度 的时候 都会 产生附带的渐变的 ...
- [iOS Animation]-CALayer 隐式动画
隐式动画 按照我的意思去做,而不是我说的. -- 埃德娜,辛普森 我们在第一部分讨论了Core Animation除了动画之外可以做到的任何事情.但是动画是Core Animation库一个非常显著的 ...
- ios开发核心动画三:隐式动画与时钟效果
一:隐式动画 #import "ViewController.h" @interface ViewController () /** <#注释#> */ @proper ...
- iOS边练边学--CALayer,非根层隐式动画,钟表练习
一.CALayer UIView之所以能显示在屏幕上,完全是因为他内部的一个图层 在创建UIView对象时,UIView内部会自动创建一个图层(即CALayer对象),通过UIView的layer属性 ...
- IOS第18天(3,CALayer隐式动画)
******隐式动画(手指拖拽Layer) #import "HMViewController.h" @interface HMViewController () @propert ...
- IOS 隐式动画(非Root Layer)
● 每一个UIView内部都默认关联着一个CALayer,我们可用称这个Layer为Root Layer(根 层) ● 所有的非Root Layer,也就是手动创建的CALayer对象,都存在着隐式动 ...
- CALayer的隐式动画
CALayer的使用 在我的理解中CALayer就是iOS中利用图层精简非交互式绘图.那么那些核心动画类.也就是变化图层的非交互式绘制规则而已.其中的本质就是将CALayer中的内容转化为map图.从 ...
- 非RootLayer的隐式动画
非RootLayer都有隐式动画,默认0.25秒. // 1.开启 [CATransaction begin]; // 2.设置关闭 YES-关闭:NO-开启 [CATransaction setDi ...
随机推荐
- Python shelve模块的使用方法
#!/usr/bin/env python3 # -*- coding: utf-8 -*- __author__ = '人生入戏' import shelve,time #写 x = shelve. ...
- Js前端传递json数组至服务器端并解析的实现。
最近做的一个小项目中需要将json数组数据传递到服务器端进行保存,现分享一下解决思路. 环境:EasyUi+Mvc 4.0 如下: 在上述截图中的红色圈起来的部分,需要在点击保存后通过一次ajax请求 ...
- JS--微信浏览器复制到剪贴板实现
由于太忙很久没写博客了,如有错误遗漏,请指出,感谢! 首先这里要注意,是微信浏览器下的解决方案,其他浏览器请自行测试. 先说复制到剪贴板主要有什么使用场景: 优惠券优惠码,需要用户复制 淘宝商品,需要 ...
- 快速搭建属于自己的数据库——mongodb
为了真实模拟一个项目上线,拥有前端后端数据库都具备的功能,我选择了mongodb作为项目的数据库支持,这里分享一些mongodb的经验心得和血的教训. mongoddb安装 在本地安装 直接通过官网下 ...
- Html语言,使用<a>标签发送电子邮件
<a>标签有一个功能是可以链接Email地址,使用mailto能让访问者便捷向网站管理者发送电子邮件. 使用mailto 属性时请参考下表: 如果mailto里同时有多个参数,第一个参数必 ...
- 【转】Spark运行过程
http://www.cnblogs.com/1130136248wlxk/articles/6289717.html
- webservice部署到服务器报错
System.Net.WebException: 基础连接已经关闭: 发送时发生错误. ---> System.IO.IOException: 从传输流收到意外的 EOF 或 0 个字节. 在 ...
- WinForm事件中的Object sender和EventArgs e参数
Windows程序有一个事件机制.用于处理用户事件. 在WinForm中我们经常需要给控件添加事件.例如给一个Button按钮添加一个Click点击事件.给TextBox文本框添加一个KeyPress ...
- Java历程-初学篇 Day08 数组
一,什么是数组 所谓数组,是相同数据类型的元素按一定顺序排列的集合.若将有限个类型相同的变量的集合命名,那么这个名称为数组名.组成数组的各个变量称为数组的分量,也称为数组的元素,有时也称为下标变量.用 ...
- Python自学笔记-面向对象相关(Mr seven)
---恢复内容开始--- http://www.cnblogs.com/wupeiqi/articles/5433893.html 类的成员可以分为三大类:字段.方法和属性. 一.字段 字段包括:普通 ...