本文为读书笔记: iOS Core Animation: Advanced Techniques
1.实际上layer
才是真正用来在屏幕上显示和做动画,UIView
仅仅是对它的一个封装,提供了一些iOS
类似于处理触摸的具体功能,以及Core Animation
底层方法的高级接口。
2.我们已经证实了图层不能像视图那样处理触摸事件,那么他能做哪些视图不能做的呢?这里有一些UIView
没有暴露出来的CALayer
的功能:
- 阴影 、圆角、带颜色的边框
- 3D变换
- 非矩形范围
- 透明遮罩
- 多级非线性动画
CALayer 属性 |
属性类型 | 作用 |
---|---|---|
contents |
id |
设置图片 |
contentsGravity |
NSString |
设置图片展示,类似于view 的contentMode
|
3.contents
id
意味着给contents
赋任何值编译都可以通过,但是,在实践中,如果你给contents
赋的不是CGImage
,那么你得到的图层将是空白的。contents
这个奇怪的表现是由Mac OS
的历史原因造成的。它之所以被定义为id
类型,是因为在Mac OS
系统上,这个属性对CGImage
和NSImage
类型的值都起作用。
使用 :layer.contents = (__bridge id)image.CGImage;
4.contentsGravity
UIView
的contentMode
,对这些属性的操作其实是对对应图层的操作,contentsGravity
就是图层的contentMode
/** Layer `contentsGravity' values. **/
CA_EXTERN NSString * const kCAGravityCenter
CA_EXTERN NSString * const kCAGravityTop
CA_EXTERN NSString * const kCAGravityBottom
CA_EXTERN NSString * const kCAGravityLeft
CA_EXTERN NSString * const kCAGravityRight
CA_EXTERN NSString * const kCAGravityTopLeft
CA_EXTERN NSString * const kCAGravityTopRight
CA_EXTERN NSString * const kCAGravityBottomLeft
CA_EXTERN NSString * const kCAGravityBottomRight
CA_EXTERN NSString * const kCAGravityResize
CA_EXTERN NSString * const kCAGravityResizeAspect
CA_EXTERN NSString * const kCAGravityResizeAspectFill
5.contentsScale
这个设置图片的每个点1个像素或者两个像素
使用 :
layer.contentsScale = image.scale;
或者
layer.contentsScale = [UIScreen mainScreen].scale;
6.contentsRect
可以用来作拼合图片(一般APP估计不会使用)
7.CALayer
给不同坐标系之间的图层转换提供了一些工具类方法:
- (CGPoint)convertPoint:(CGPoint)point fromLayer:(CALayer *)layer;
- (CGPoint)convertPoint:(CGPoint)point toLayer:(CALayer *)layer;
- (CGRect)convertRect:(CGRect)rect fromLayer:(CALayer *)layer;
- (CGRect)convertRect:(CGRect)rect toLayer:(CALayer *)layer;
8.阴影效果
shadowOpacity
: 是一个必须在0.0(不可见)和1.0(完全不透明)之间的浮点数。shadowColor
: 属性控制着阴影的颜色,和borderColor
和backgroundColor
一样,它的类型也是CGColorRef。阴影默认是黑色。shadowOffset
属性控制着阴影的方向和距离。它是一个CGSize
的值,宽度控制这阴影横向的位移,高度控制着纵向的位移。shadowOffset
的默认值是 {0, -3},意即阴影相对于Y轴有3个点的向上位移(以为shadow最早出现在MacOS,在MacOS上坐标轴和iOS是相反的)。shadowPath
: 如果事先知道阴影的形状,可以设置shadowPath
来提高性能。
9.opacity
相当于view
的alpha
10.layer.shouldRasterize
这是由透明度的混合叠加造成的,当你显示一个50%透明度的图层时,图层的每个像素都会一半显示自己的颜色,另一半显示图层下面的颜色。这是正常的透明度的表现。但是如果图层包含一个同样显示50%透明的子图层时,你所看到的视图,50%来自子视图,25%来了图层本身的颜色,另外的25%则来自背景色。
在我们的示例中,按钮和表情都是白色背景。虽然他们都是50%的可见度,但是合起来的可见度是75%,所以标签所在的区域看上去就没有周围的部分那么透明。所以看上去子视图就高亮了,使得这个显示效果都糟透了。
解决办法:
bottomView.layer.shouldRasterize = YES;
bottomView.layer.rasterizationScale = [UIScreen mainScreen].scale;
11.CGAffineTransform
仿射
的意思是无论变换矩阵用什么值,图层中平行的两条线在变换之后任然保持平行- 如果需要混合两个已经存在的变换矩阵,就可以使用如下方法,在两个变换的基础上创建一个新的变换:
CGAffineTransformConcat(CGAffineTransform t1, CGAffineTransform t2);
12.CATransform3D
的m34
元素,用来做透视
详见
- (void)viewDidLoad { [super viewDidLoad];
//create a new transform
CATransform3D transform = CATransform3DIdentity;
//apply perspective
transform.m34 = - 1.0 / 500.0;
//rotate by 45 degrees along the Y axis
transform = CATransform3DRotate(transform, M_PI_4, 0, 1, 0);
//apply to layer
self.layerView.layer.transform = transform; }
-
创建一个3D的正方体](https://zsisme.gitbooks.io/ios-/content/chapter5/solid-objects.html))
14.Core Graphics
直接向原始的CALyer
的内容中绘制一个路径,相比直下,使用CAShapeLayer
有以下一些优点:
- 渲染快速。CAShapeLayer使用了硬件加速,绘制同一图形会比用Core Graphics快很多。
- 高效使用内存。一个CAShapeLayer不需要像普通CALayer一样创建一个寄宿图形,所以无论有多大,都不会占用太多的内存。
- 不会被图层边界剪裁掉。一个CAShapeLayer可以在边界之外绘制。你的图层路径不会像在使用Core Graphics的普通CALayer一样被剪裁掉(如我们在第二章所见)。
- 不会出现像素化。当你给CAShapeLayer做3D变换时,它不像一个有寄宿图的普通图层一样变得像素化。
15.UILabel
: 借助图层代理直接将字符串使用Core Graphics
写入图层的内容
16.宿主图层
: 指的是view
的layer
,既self.view.layer
,可以通过下面的方法改变
+ (Class)layerClass
{
return [CATextLayer class];
}
17.CATextLayer
替换 UILabal
](https://zsisme.gitbooks.io/ios-/content/chapter6/CATextLayer.html))
18.CATransformLayer
:创建一个立方体图层
19.CAGradientLayer
colors
: 保存的各种颜色locations
: 颜色的作用范围,这个范围数组需要个颜色数组保持相同 , 当只有两个颜色的时候设置gradientLayer.locations = @[@0,@1];
是均分两个视图startPoint
,开始的点,(0 , 0)
代表左上角endPoint
,结束的店,(1 , 1)
代表右下角
20.CATiledLayer
: 把一个大图裁剪成小图并加载,一般用在地图,或者加载大的图片.
-
Transactions(事务)
: 实际上是Core Animation用来包含一系列属性动画集合的机制,任何用指定事务去改变可以做动画的图层属性都不会立刻发生变化,而是当事务一旦提交的时候开始用一个动画过渡到新值。
begin
: 开始commit
: 结束+ (void)setCompletionBlock:(nullable void (^)(void))block;
: 代码完成块
例1
- (void)viewDidLoad
{
[super viewDidLoad];
self.colorLayer = [CALayer layer];
self.colorLayer.frame = CGRectMake(0.0f, 0.0f, 150.0f, 150.0f);
self.colorLayer.backgroundColor = [UIColor blueColor].CGColor;
//add it to our view
[self.containerView.layer addSublayer:self.colorLayer];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
//begin a new transaction
[CATransaction begin];
//set the animation duration to 1 second
[CATransaction setAnimationDuration:5.0];
//randomize the layer background color
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;
//commit the transaction
[CATransaction commit];
}
例2
- (void)viewDidLoad{ [super viewDidLoad]; //create sublayer self.colorLayer = [CALayer layer]; self.colorLayer.frame = CGRectMake(50.0f, 50.0f, 100.0f, 100.0f); self.colorLayer.backgroundColor = [UIColor blueColor].CGColor; //add a custom action CATransition *transition = [CATransition animation]; transition.type = kCATransitionPush; transition.subtype = kCATransitionFromLeft; self.colorLayer.actions = @{@"backgroundColor": transition}; //add it to our view [self.layerView.layer addSublayer:self.colorLayer];}- (IBAction)changeColor{ //randomize the layer background color 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;}
22.隐式动画是如何实现的
- 图层首先检测它是否有委托,并且是否实现CALayerDelegate
协议指定的-actionForLayer:forKey
方法。如果有,直接调用并返回结果。- 如果没有委托,或者委托没有实现-actionForLayer:forKey
方法,图层接着检查包含属性名称对应行为映射的actions
字典。- 如果actions字典
没有包含对应的属性,那么图层接着在它的style
字典接着搜索属性名。- 最后,如果在style
里面也找不到对应的行为,那么图层将会直接调用定义了每个属性的标准行为的-defaultActionForKey:
方法。
23.CATransition
响应CAAction
协议,并且可以当做一个图层行为.行为通常是一个被Core Animation隐式调用的显式动画对象。
24.presentationLayer
: 呈现图层 ,用户真正看到的图层(相当于View)
modelLayer
: 图层模型 (相当于Model)
Core Animation
: 相当于控制器
大多数情况下,你不需要直接访问呈现图层,你可以通过和模型图层的交互,来让Core Animation更新显示。两种情况下呈现图层会变得很有用,一个是同步动画,一个是处理用户交互。
- 如果你在实现一个基于定时器的动画(见第11章“基于定时器的动画”),而不仅仅是基于事务的动画,这个时候准确地知道在某一时刻图层显示在什么位置就会对正确摆放图层很有用了。
- 如果你想让你做动画的图层响应用户输入,你可以使用-hitTest:方法(见第三章“图层几何学”)来判断指定图层是否被触摸,这时候对呈现图层而不是模型图层调用-hitTest:会显得更有意义,因为呈现图层代表了用户当前看到的图层位置,而不是当前动画结束之后的位置。
25.缓冲函数CAMediaTimingFunction
自定义缓冲函数
+ (instancetype)functionWithControlPoints:(float)c1x :(float)c1y :(float)c2x :(float)c2y;
- (instancetype)initWithControlPoints:(float)c1x :(float)c1y :(float)c2x :(float)c2y;
- 那么该如何使用缓冲方程式呢?
首先需要设置CAAnimation的timingFunction
属性,是CAMediaTimingFunction
类的一个对象。如果想改变隐式动画的计时函数,同样也可以使用CATransaction
的+setAnimationTimingFunction:
方法。
26.RunLoop
NSDefaultRunLoopMode
- 标准优先级
NSRunLoopCommonModes
- 高优先级
UITrackingRunLoopMode
- 用于UIScrollView和别的控件的动画
CADisplayLink
: 屏幕更新执行一次刷新一次
self.timer = [CADisplayLink displayLinkWithTarget:self selector:@selector(step:)];
[self.timer addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
[self.timer addToRunLoop:[NSRunLoop mainRunLoop] forMode:UITrackingRunLoopMode];
NSTimer
:标准的时间
self.timer = [NSTimer timerWithTimeInterval:1/60.0 target:self selector:@selector(step:) userInfo:nil repeats:YES];[[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];