记录寄己走过的路

iOS 核心动画—「CALayer图层」

本篇文章主要从【Core Animation 之 CALayer】学习总结。
在「时间 & 知识 」有限内,总结的文章难免有「未全、不足 」的地方,还望各位好友指出,以提高文章质量。

目录:

  1. UIView和CALayer的类定义
  2. UIView和CALayer的区别和选择
  3. CALayer的基本操作
  4. CATransform3D
  5. CGAffineTransform
  6. 真正的高阶技巧 iOS Core Animation

1.UIView和CALayer的类定义

1.CALayer的介绍
CALayer 是定义在 QuartzCore 框架中,从下图可以看出UIView内部定义了一个CALayer对象,它是用来在屏幕上显示内容展示的矩形区域;
CALayer是个与UIView很类似的概念,同样有backgroundColor、frame等相似的属性,我们可以将UIView看做一种特殊的CALayer。但实际上UIView是对CALayer封装,在CALayer的基础上再添加交互功能。UIView的显示必须依赖于CALayer。我们同样可以跟新建view一样新建一个layer,然后添加到某个已有的layer上,同样可以对layer调整大小、位置、透明度等。一layer可以有两种用途一是对view相关属性的设置,包括圆角、阴影、边框等参数;二是实现对view的动画操控。因此对一个view进行动画,本质上是对该view的.layer进行动画操作;

UIView内部CALayer对象.png

2.UIView和CALayer的区别和选择

UIView和CALayer区别
1.在创建UIView对象时,UIView内部会自动创建一个图层(即CALayer对象),CALayer 在背后提供内容的绘制和显示;两者都有树状层级结构,layer 内部有 SubLayers,View 内部有 SubViews.但是 Layer 比 View 多了个AnchorPoint(锚点)

2.当UIView需要显示到屏幕上时(UIView 做为 Layer 的 CALayerDelegate,View 显示内容由CALayer 的 display),会调用drawRect:方法进行绘图,并且会将所有内容绘制在自己的图层上,绘图完毕后,系统会将图层拷贝到屏幕上,于是就完成了UIView的显示。
换句话说,UIView本身不具备显示的功能,是因为它内部的图层(CALayer)才有显示功能

3.Layer 的 frame 是由它的 bounds、position、anchorPoint 和 transform 共同决定的;View 的 frame 只是简单的返回 Layer的 frame,同样 View 的 bounds 和 center 也是返回 Layer 的一些属性。

4.UIView 多了一个事件处理的功能,也就是说 UIView 可以处理用户的触摸事件,而 CALayer 不可以

UIView和CALayer选择
通过CALayer,也能做出和 UIImageView 一样的效果,相比较UIView多了一个事件处理的功能;

所以,如果显示出来的东西需要跟用户进行交互的话,用UIView;如果不需要进行交互,用UIView和CALayer都可以;

当然,CALayer 的性能会高一些,因为它少了事件处理的功能,更轻量级(实际开发中还是建议使用UIView,可扩展性强);

2.CALayer的基本操作

1.CALayer的常用属性

属性 描述
bounds 图层大小
position 图层中心点位置,相当于UIView的center
opacity 透明度,相当于UIView的alpha
anchorPoint 和中心position重合的点,称为锚点,范围在(0~1,0~1),默是(0.5,0.5)
contents image添加到layer的contents
opacity 透明度,相当于UIView的alpha
contentsRect 设置图片显示的尺寸,取值0~1(x0, y0, W1, H1),如 CGRectMake(0, 0, 1, 0.5);只将图像的上半部分显示在整个layer中;
CATransform3D 形变属性(设置平移、缩放和旋转时的 3D效果)
cornerRadius / masksToBounds 圆角半径 / 属性为YES才显示圆角效果

2.创建自定义的CALayer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
// layer基本使用
- (void)layerWithUIView{
 // 自定义图层
    CALayer *layer = [CALayer layer];
    layer.bounds = CGRectMake(00100100);
    layer.contents = (id)[UIImage imageNamed:@"阿狸头像"].CGImage;?
    layer.backgroundColor = [UIColor redColor].CGColor;?
    layer.position = self.view.center;
    layer.anchorPoint = CGPointMake(0.51);
    // 默认是自带阴影的效果, 默认为0.
    self.redView.layer.shadowOpacity = 1;
    // 添加图层到根图层
    [self.view.layer addSublayer:layer];
    ***********其它属性**************** 
    
    // 设置阴影的颜色
    layer.shadowColor = [UIColor blackColor].CGColor;
    // 设置阴影的偏移量((x,y) 例:右下)
    layer.shadowOffset = CGSizeMake(1010);
    // 设置阴影的模糊程度
    layer.shadowRadius = 10;
    // 设置边框的宽度(注意:是往里面走的) 和 颜色
    layer.borderWidth = 5;
    layer.borderColor = [UIColor blueColor].CGColor;
    
    // 设置圆角半径
    layer.cornerRadius = 50;
    // 图层中绘制的图片无法正确显示,解决:把超过根层以外的东西剪切掉(UIView自带的层,我们称为是根层)
    layer.masksToBounds = YES;
    
    // 设置图层的代理
    layer.delegate = self;
    // setNeedsDisplayh会调用drawRect:方法
    [layer setNeedsDisplay];
}
``` 
注解:
- 1、阴影效果无法 和 `masksToBounds`同时使用;
- 2、裁剪:`masksToBounds` 是 `CALayer` 的属性;`clipsToBounds` 是 `UIView` 的属性;
- 3、`CGImage` 和 `CGColor` 的解释:
- `CALayer`是定义在`QuartzCore`框架中的
`CGImageRef`、`CGColorRef`两种数据类型是定义在`CoreGraphics`框架中的
- `UIColor`、`UIImage`是定义在`UIKit`框架中的
`QuartzCore`框架和`CoreGraphics`框架是可以跨平台使用的,在`iOS`和`Mac OS X`上都能使用,但是`UIKit`只能在`iOS`中使用,为了保证可移植性,`QuartzCore`不能使用`UIImage`、`UIColor`,只能使用`CGImageRef`、`CGColorRef`,即需要:`.CGImage` 和 `.CGColor`.
#### 3.CATransform3D
>注意:使用KVC快速设置时,Key Paths值一定不要写错
```objc
// 触摸开始调用
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
// CATransform3D,设置平移、缩放和旋转时的 3D效果
[UIView animateWithDuration:1 animations:^{
// 方式一:CATransform3D普通设置
// 平移 (CGFloat sx, CGFloat sy, CGFloat sz)
self.imageView.layer.transform = CATransform3DTranslate(self.imageView.layer.transform, 10, 0, 0);
// 缩放
self.imageView.layer.transform = CATransform3DScale(self.imageView.layer.transform, 1.2, 1.2, 0);
// 旋转
self.imageView.layer.transform = CATransform3DMakeRotation(M_PI, 0, 0, 1);
// 方式二:CATransform3D KVC快速设置
// 把结构体转成对象
//NSValue *value = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(M_PI, 0, 0, 1)];
//[self.imageView.layer setValue:value forKeyPath:@"transform"];
// 通过KVC快速平移(translation)、缩放(sacle)、旋转(rotation)
// 平移
[self.imageView.layer setValue:@(100) forKeyPath:@"transform.translation.y"];
// 缩放
[self.imageView.layer setValue:@(0.5) forKeyPath:@"transform.scale"];
// 旋转
[self.imageView.layer setValue:@(M_PI_4) forKeyPath:@"transform.rotation"];
}];
}

PS.
在这里我就不用图片了,我就劳动一下吧(图片的不好复制不是吗),福利奉上转场效果key paths

Transform field value key paths

Field Key Path Description
translation.x 设置为一个NSNumber对象的值是沿着x轴平移。
translation.y 设置为一个NSNumber对象的值沿y轴平移。
translation.z 设置为一个NSNumber对象的值沿z轴平移。
translation 设置为一个NSValue对象包含一个NSSize或CGSize数据类型。数据类型表示将在x和y轴。
scale.x 设置为一个NSNumber对象的值是x轴缩放。
scale.y 设置为一个NSNumber对象的值是y轴缩放。
scale.z 设置为一个NSNumber对象的值是z轴缩放。
scale 设置为一个NSNumber对象的值是所有三个规模因素的平均值。
rotation.x 设置为一个NSNumber对象的值是旋转,弧度,x轴。
rotation.y 设置为一个NSNumber对象的值是旋转,弧度,y轴。
rotation.z 设置为一个NSNumber对象的值是旋转,弧度,z轴。
rotation 设置为一个NSNumber对象的值是旋转,弧度,z轴。这个字段是一样设置旋转。z域。

4.CGAffineTransform

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 旋转,参数指定为弧度,M_PI <-> 180
CGAffineTransformMakeRotation(CGFloat angle);
// 缩放, sx:指x轴缩放的比例,sy 在y轴上的缩放比例
CGAffineTransformMakeScale(CGFloat sx, CGFloat sy);
// 平移 tx:在x轴上平移 ty 是在y上平移
CGAffineTransformMakeTranslation(CGFloat tx, CGFloat ty);
// 让你的view回到最原始的状态,没有缩放,没有旋转,没有平移
ballView.transform = CGAffineTransformIdentity;
// 指定在y轴上 平移
ballView.transform = CGAffineTransformMakeTranslation(0, -300);

真正的高阶技巧 iOS Core Animation

强力推荐👍《iOS Core Animation: Advanced Techniques》这本书很深入的将了Core Animation的原理性的东西,是一本讲解Core Animation原理非常深入的书,如果把整本书全部读完,理解,我相信iOS 中的
动画就是件很轻松的事情了,可惜是英文的;

在网上也找到了这本书完整的中文翻译,如果感兴趣,可以去看看 ios核心动画高级技巧

iOS Core Animation.png

最后附上这张图:
CALayer属性.png

期待


  • 如果在阅读过程中遇到 error,希望你能 Issues 我,谢谢。

  • 如果你想为【本文相关】分享点什么,也希望你能 Issues 我,我非常想为这篇文章增加更多实用的内容,谢谢。

  • 「博客原文」,对本文我会【不定时、持续更新、一些 学习心得与文章、实用才是硬道理】^_^.

Write in the first【写在最前】


iOS 动画主要是指 Core Animation 框架。官方使用文档地址为: Core Animation Guide

Core Animation 是iOS和macOS平台上负责图形渲染与动画的基础框架。Core Animation 可以作用与动画视图或者其他可视元素,为你完成了动画所需的大部分绘帧工作。你只需要配置少量的动画参数(如开始点的位置和结束点的位置)即可使用 Core Animation 的动画效果。

Core Animation 将大部分实际的绘图任务交给了图形硬件来处理,图形硬件会加速图形渲染的速度。|这种自动化的图形加速技术让动画拥有更高的帧率并且显示效果更加平滑,不会加重CPU的负担而影响程序的运行速度。

本篇文章主要从【iOS动画 Core Animation】学习总结。
在「时间 & 知识 」有限内,总结的文章难免有「未全、不足 」的地方,还望各位好友指出,以提高文章质量。

目录:

  1. 系统自带的 animationImages
  2. UIView代码块加Block
  3. UIView [begin commit]模式
  4. 使用CoreAnimation中的类
    1.CATransition 转场动画
    1.CATransaction动画事务
    2.CABasicAnimation 基础动画
    2.CASpringAnimation 弹簧动画
    3.CAKeyframeAnimation 关键帧动画
    4.CAAnimationGroup 动画组
    5.AnimationWithKeyPath的值
  5. 物理动效(重力、碰撞、吸附、推力、关联)
    1.UICollisionBehavior碰撞
    2.UISnapBehavior吸附
    3.UIPushBehavior推力
    4.UIAttachmentBehavior关联
  6. 粒子系统
  7. facebook pop动画

iOS动画调用方式

1.系统自带的 animationImages

1
2
3
4
UIImageView *imageView;
imageView.animationImages = @[image1,image2...];
[imageView startAnimating];
[imageView stopAnimating];

2.UIView代码块加Block

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
 Duration 动画持续时间
 animations 动画过程
 */
[UIView animateWithDuration:<#(NSTimeInterval)#> animations:<#^(void)animations#>];
// 执行动画 和 动画完成的回调
[UIView animateWithDuration:<#(NSTimeInterval)#> animations:<#^(void)animations#> completion:<#^(BOOL finished)completion#>];
/**
 delay 等待时间
 options 动画类型
 */
[UIView animateWithDuration:<#(NSTimeInterval)#> delay:<#(NSTimeInterval)#> options:<#(UIViewAnimationOptions)#> animations:<#^(void)animations#> completion:<#^(BOOL finished)completion#>];
/**
 弹性动画
 damping 阻尼,范围0-1,阻尼越接近于0,弹性效果越明显
 velocity 弹性复位的速度
 */
[UIView animateWithDuration:<#(NSTimeInterval)#> delay:<#(NSTimeInterval)#> usingSpringWithDamping:<#(CGFloat)#> initialSpringVelocity:<#(CGFloat)#> options:<#(UIViewAnimationOptions)#> animations:<#^(void)animations#> completion:<#^(BOOL finished)completion#>];
// 关键帧动画
[UIView animateKeyframesWithDuration:<#(NSTimeInterval)#> delay:<#(NSTimeInterval)#> options:<#(UIViewKeyframeAnimationOptions)#> animations:<#^(void)animations#> completion:<#^(BOOL finished)completion#>];

3.UIView [begin commit]模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#pragma mark - 使用UIView [begin,commit]模式
- (void)uiViewAnimation {
// 可以嵌套但是必须成对出现
UIViewAnimationTransition type = UIViewAnimationTransitionNone;
[UIView beginAnimations:nil context:nil];
// 动画的持续时间
[UIView setAnimationDuration:1];
// 动画类型
[UIView setAnimationTransition:type forView:_redView cache:YES];
// 动画代理
//[UIView setAnimationDelegate:self];
//[UIView setAnimationWillStartSelector:@selector(annimationStart)];
//[UIView setAnimationDidStopSelector:@selector(annimationStop)];
[UIView commitAnimations];
}
- (void)animationDidStart:(CAAnimation *)anim {
NSLog(@"%s, line = %d",__FUNCTION__,__LINE__);
}
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
NSLog(@"%s, line = %d",__FUNCTION__,__LINE__);
}

4.使用CoreAnimation中的类

核心动画继承结构.png

1.CATransition 转场动画

CATransition属性 描述
type 过渡的类型
subType 过渡的方向
startProgress 动画起点(在整体动画的百分比)
endProgress 动画终点(在整体动画的百分比)
addAnimation: forKey: 把转场动画添加到layer上

创建步骤
1.创建动画对象
2.设置转场类型
3.给图层添加动画

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#pragma mark - 交叉淡化过渡
-(void)fadeAnimation{
[self changeShowContens];
// 1.创建动画对象
CATransition *anima = [CATransition animation];
// 2.设置转场类型
anima.type = kCATransitionFade;// 设置动画的类型
//anima.type = @"cube";// 设置动画的类型
anima.subtype = kCATransitionFromRight; // 设置动画的方向
//anima.startProgress = 0.3;// 设置动画起点
//anima.endProgress = 0.8;// 设置动画终点
anima.duration = 1.0f;// 设置动画执行时长
// 3.给图层添加动画
[_imageView.layer addAnimation:anima forKey:@"fadeAnimation"];
}

效果:
转场动画.gif转场动画.gif

2.CATransition转场动画过渡类型

在这里我就不用图片了,我就劳动一下吧(图片的不好复制不是吗),奉上转场效果 api

类型字符串 效果说明 关键字 方向
fade 交叉淡化过渡 YES
push 新视图把旧视图推出去 YES
moveIn 新视图移到旧视图上面 YES
reveal 将旧视图移开,显示下面的新视图 YES
cube 立方体翻滚效果
oglFlip 上下左右翻滚效果
suckEffect 收缩效果,如一块布被抽走 NO
rippleEffect 水滴效果 NO
pageCurl 向上翻页效果
pageUnCurl 向下翻页效果
cameraIrisHollowOpen 相机镜头打开效果 NO
cameraIrisHollowClose 相机镜头关闭效果 NO
动画常用属性 描述
rotation.x 设置为一个NSNumber对象的值是旋转,弧度,x轴。
duration 动画的持续时间
beginTime 动画的开始时间
repeatCount 动画的重复次数
autoreverses 执行的动画按照原动画返回执行
timingFunction 控制动画的显示节奏
Linear 匀速,EaseIn 先慢后快,EaseOut 先快后慢,EaseInEaseOut 先慢后快再慢,Default 默认中间比较快

3.CATransaction动画事务

事务(CATransaction)负责协调多个动画原子更新的显示操作,是动画里面的一个基本单元,动画的产生必然伴随着layer的Animatable属性的变化,而layer属性的变化必须属于某一个事务。
因此 ,核心动画依赖于事务。
可以通过事物关闭隐式动画:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
1、事务的作用:保证一个或多个layer的一个或多个属性变化同时进行。
2、事务的种类:隐式和显示
3、隐式:没有明显调用事务的方法,由系统自动生成事务。比如上面提到的设置一个layer的position属性,则会在当前线程自动生成一个事务,并在下一个runLoop中自动commit.
通过如下方法可以关闭隐式动画的设置:
[CATransaction begin];// 开启事务
[CATransaction setDisableActions:YES];// 关闭隐式动画
self.myview.layer.position = CGPointMake(10, 10);
[CATransaction commit];// 提交事务
4、显示:明显调用事务的方法[CATransaction begin] 和 [CATransaction commit],必须写在两者之间.
5、CA事务的可设置属性(会覆盖隐式动画的设置)
animationDuration:动画时间
animationTimingFunction:动画时间曲线
disableActions:是否关闭动画
completionBlock:动画执行完毕的回调
事务支持嵌套使用:当最外层的事务commit后动画才会开始。
6
通过修改layer上的属性产生的动画效果,称之为 隐式动画
通过手动添加动画对象产生的动画,称之为 显式动画

4.CABasicAnimation 基础动画

CABasicAnimation重要属性 描述
fromValue keyPath对应的初始值
toValue keyPath对应的结束值
removedOnCompletion 动画完成时,是否删除动画
fillMode 设置让动画效果最后执行状态

基础动画创建步骤
1、初始化动画并设置动画属性
2、设置动画属性初始值(可以省略)、结束值以及其他动画属性
3、给图层添加动画

我们先看下面这个移动动画实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#pragma mark - 位移动画
- (void)positionAnimation {
// 1.创建动画对象并设置动画属性
CABasicAnimation *basicAnimation = [CABasicAnimation animationWithKeyPath:@"position"];
// 2.设置动画 初始值(可以省略) 和 结束值
// 把CGPoint转换成id类型,使用NSValue
basicAnimation.fromValue = [NSValue valueWithCGPoint:CGPointMake(50, kScreenHeight/2)];
basicAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(kScreenWidth- 50, kScreenHeight/2)];
// 设置动画的其他属性值
// 动画执行时长2秒(默认0.2s)
basicAnimation.duration = 1.0f;
// 设置重复次数,HUGE_VALF可看做无穷大
//basicAnimation.repeatCount = HUGE_VALF;
// 设置重复时间(重复时间 / 执行时长 = 重复次数)
//basicAnimation.repeatDuration = 4;
// 设置延迟执行2秒
//basicAnimation.beginTime = CACurrentMediaTime() + 2;
// 动画时间曲线 kCAMediaTimingFunctionLinear 线性变换 平缓
//basicAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
// 自动反转(怎么去就怎么回来)
//basicAnimation.autoreverses = YES;
// 设置动画代理
//basicAnimation.delegate = self;
/**
如果fillMode=kCAFillModeForwards和removedOnComletion=NO,那么在动画执行完毕后,图层会保持显示动画执行后的状态。但在实质上,图层的属性值还是动画执行前的初始值,并没有真正被改变。(不设置这两个属性值默认回到原始位置)
2016-06-15 13:15:24.350 iOS常用动画[14625:325800] 动画前X = 137.500000
2016-06-15 13:15:24.350 iOS常用动画[14625:325800] 动画后X = 137.500000
*/
//basicAnimation.removedOnCompletion = NO;// 动画完成时,会自动删除动画
//basicAnimation.fillMode = kCAFillModeForwards;// 设置让动画效果最后执行状态
// 3.给图层添加动画(注意key相当于给动画进行命名,以后获得该动画时可以使用此名称获取)
[self.redView.layer addAnimation:basicAnimation forKey:@"positionAnimation"];
// 移除动画
//[self.redView.layer removeAnimationForKey:@"positionAnimation"];
//[self.redView.layer removeAllAnimations];
}

效果:
CABasicAnimation基础动画.gif

CASpringAnimation 弹簧动画
属性 描述
mass 质量,影响图层运动时的弹簧惯性,质量越大,弹簧拉伸和压缩的幅度越大
stiffnes 刚度系数(劲度系数/弹性系数),刚度系数越大,形变产生的力就越大,运动越快
damping 阻尼系数,阻止弹簧伸缩的系数,阻尼系数越大,停止越快
initivelocity 初始速率,动画视图的初始速度大小;速率为正数时,速度方向与运动方向一致,速率为负数时,速度方向与运动方向相反
settingDuration 结算时间(根据动画相关参数估算弹簧开始运动到停止的时间,动画设置的时间最好根据此时间来设置)

CASpringAnimationUIView的SpringAnimation对比:
CASpringAnimation 可以设置更多弹簧动画效果的属性,可以实现更复杂的弹簧效果,可以和其他动画组合
UIView的SpringAnimation实际上就是通过CASpringAnimation来实现的。

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#pragma mark - 弹簧动画
- (void)springAnimation {
CASpringAnimation * springAnimation = [CASpringAnimation animationWithKeyPath:@"transform.scale"];
// 质量,越大弹簧幅度越大
springAnimation.mass = 10.0;
// 弹性系数,越大运动越快
springAnimation.stiffness = 500;
// 阻尼系数,越大停止越快
springAnimation.damping = 100.0;
// 初始速率,正方向相同、负方向相反
springAnimation.initialVelocity = 30.f;
springAnimation.duration = springAnimation.settlingDuration;
springAnimation.toValue = [NSNumber numberWithFloat:1.5];
//springAnimation.removedOnCompletion = NO;
//springAnimation.fillMode = kCAFillModeForwards;
springAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[self.redView.layer addAnimation:springAnimation forKey:@"boundsAni"];
}

效果:
CASpringAnimation弹簧动画.gif

3.CAKeyframeAnimation 关键帧动画

可以让我们在更细的粒度上控制动画的行为,关键帧动画需要指定几个关键的点,从而让动画沿着这些点运动,这几个点就称之为 关键帧

CAKeyframeAnimation属性 描述
values 指定关键点的值
path 可以设置一个CGPathRef / CGMutablePathRef,让层跟着路径移动。path只对CALayer的anchorPoint和position起作用。如果你设置了path,那么values将被忽略.
keyTimes 是走到某一个关键点花费的时间百分比(0~1),keyTimes中的每一个时间值都对应values中的每一帧(两个数组的个数必须一致),当keyTimes没有设置的时候,各个关键帧的时间是平分的。
通过 设置不同的属性值values 动画 关键帧动画创建第一种方式
通过 绘制路径path 动画 关键帧动画创建第二种方式

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 角度转弧度
#define angleToRadian(angle) ((angle) / 180.0 * M_PI)
#pragma mark 关键帧动画values
- (void)valuesAnimation {
// 1.创建动画对象
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation"];
// 2.设置动画属性值
//NSValue *value1 = [NSValue valueWithCGPoint:<#(CGPoint)#>];
animation.values = @[@(angleToRadian(-5)),@(angleToRadian(5)),@(angleToRadian(-5))];
animation.duration = 1;
animation.repeatCount = MAXFLOAT;
//animation.keyTimes = @[@(0.0),@(1),@(0.1)];
// 如果不用反转,也可以在values里面写
//animation.autoreverses = YES;
// 动画结束时的状态(不设置回到原始位置)
//anim.removedOnCompletion = NO;
//anim.fillMode = kCAFillModeForwards;
// 3.给图层添加动画
[self.iconImage.layer addAnimation:animation forKey:@"valuesAnimation"];
}

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#pragma mark 关键帧动画第二种方式
- (void)translationAnimation2 {
    // 1.创建关键帧动画并设置动画属性
    CAKeyframeAnimation *moveAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    moveAnimation.duration = 2;
    // 2.设置关键帧动画的行进路径(绘制贝塞尔曲线)
    moveAnimation.path = self.path.CGPath;
    
    // 3.添加动画到图层,添加动画后就会执行动画
    [self.ballView.layer addAnimation:moveAnimation forKey:nil];
    // 设置最终的状态(不设置就是回到原始位置)
    self.ballView.layer.position = CGPointMake(330200);
}
// 绘制贝塞尔曲线
- (void)drawRect:(CGRect)rect {
    self.path = [[UIBezierPath alloc]init];
    [[UIColor redColor] setStroke];
    self.path.lineWidth = 5;
    
    [self.path moveToPoint:CGPointMake(30200)];
    [self.path addCurveToPoint:CGPointMake(330200) controlPoint1:CGPointMake(10050) controlPoint2:CGPointMake(200300)];
    [self.path stroke];
}

效果
.

CAKeyframeAnimation关键帧动画.gif

4.CAAnimationGroup 动画组

CAAnimationGroup属性 描述
animations 用来保存一组动画对象的NSArray
CFTimeInterval 时间间隔

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#pragma mark 动画组(同时)
- (void)groupAnimation1 {
// 位移动画
CAKeyframeAnimation *animation1 = [CAKeyframeAnimation animationWithKeyPath:@"position"];
NSValue *value0 = [NSValue valueWithCGPoint:CGPointMake(0, kScreenHeight/2-50)];
NSValue *value1 = [NSValue valueWithCGPoint:CGPointMake(kScreenWidth/3, kScreenHeight/2-50)];
NSValue *value2 = [NSValue valueWithCGPoint:CGPointMake(kScreenWidth/3, kScreenHeight/2+50)];
NSValue *value3 = [NSValue valueWithCGPoint:CGPointMake(kScreenWidth*2/3, kScreenHeight/2+50)];
NSValue *value4 = [NSValue valueWithCGPoint:CGPointMake(kScreenWidth*2/3, kScreenHeight/2-50)];
NSValue *value5 = [NSValue valueWithCGPoint:CGPointMake(kScreenWidth, kScreenHeight/2-50)];
animation1.values = @[value0,value1,value2,value3,value4,value5];
// 缩放动画
CABasicAnimation *animation2 = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
animation2.fromValue = [NSNumber numberWithFloat:0.8f];
animation2.toValue = [NSNumber numberWithFloat:2.0f];
// 旋转动画
CABasicAnimation *animation3 = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
animation3.toValue = [NSNumber numberWithFloat:M_PI *4];
// 创建动画组
CAAnimationGroup *groupAnimation = [CAAnimationGroup animation];
groupAnimation.animations = @[animation1,animation2,animation3];
groupAnimation.duration = 4;
[self.redView.layer addAnimation:groupAnimation forKey:@"groupAnimation"];
}

效果
CAAnimationGroup动画组.gif

5.AnimationWithKeyPath的值

在这里我就不用图片了,我就劳动一下吧(图片的不好复制不是吗),福利奉上转场效果 key paths
Transform field value key paths

Field Key Path Description
rotation.x 设置为一个NSNumber对象的值是旋转,弧度,x轴。
rotation.y 设置为一个NSNumber对象的值是旋转,弧度,y轴。
rotation.z 设置为一个NSNumber对象的值是旋转,弧度,z轴。
rotation 设置为一个NSNumber对象的值是旋转,弧度,z轴。这个字段是一样设置旋转。z域。
scale.x 设置为一个NSNumber对象的值是x轴的比例因子。
scale.y 设置为一个NSNumber对象的值是y轴的比例因子。
scale.z 设置为一个NSNumber对象的值是z轴的比例因子。
scale 设置为一个NSNumber对象的值是所有三个规模因素的平均值。
translation.x 设置为一个NSNumber对象的值是沿着x轴。
translation.y 设置为一个NSNumber对象的值沿y轴。
translation.z 设置为一个NSNumber对象的值沿z轴。
translation 设置为一个NSValue对象包含一个NSSize或CGSize数据类型。数据类型表示将在x和y轴。

核心动画综合案例

核心动画综合案例.gif

5.物理动效(重力、碰撞、吸附、推力、关联)

1
2
3
4
5
6
ios7之后提供的物理动效
UIGravityBehavior 重力
UICollisionBehavior 碰撞
UISnapBehavior    吸附
UIPushBehavior    推力
UIAttachmentBehavior 关联

以下示例,我们就直接来代码,看效果了,中间会有部分解释;

1.UICollisionBehavior碰撞,示例:模仿重力 + 碰撞 的行为
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 先指定一个参考视图来初始化animator
self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
#pragma mark 模仿重力 和 碰撞效果
- (void)animations {
    // 添加重力
    UIGravityBehavior *gravity = [[UIGravityBehavior alloc]initWithItems:@[self.ballView]];
    // 添加碰撞
    UICollisionBehavior *collision = [[UICollisionBehavior alloc]initWithItems:@[self.ballView]];
    // 把参考视图的边界作为我的碰撞边界
    collision.translatesReferenceBoundsIntoBoundary = YES;
    
    // 添加一个自定义的行为,修改动效参数
    UIDynamicItemBehavior *itemBehavior = [[UIDynamicItemBehavior alloc]initWithItems:@[self.ballView]];
    /**
     弹性系数
     density 1.0             密度
     elasticity 0.0          弹性
     friction 0.0            摩擦
     resistance 0.0          阻力
     */
    itemBehavior.elasticity = 0.5;
    // 把重力和碰撞行为添加到动画执行者中
    [self.animator addBehavior:gravity];
    [self.animator addBehavior:collision];
    [self.animator addBehavior:itemBehavior];
}

效果:
模仿重力和碰撞效果.gif

2.UISnapBehavior吸附,示例:模仿吸附 + 重力 的行为
1
2
3
4
5
6
7
8
9
10
11
12
- (void)doAnimation{
    //[self.animator removeAllBehaviors];
    // 添加一个吸附行为,指定吸附的点
    UISnapBehavior *snap = [[UISnapBehavior alloc]initWithItem:self.ballView snapToPoint:CGPointMake(self.view.frame.size.width *0.5self.view.frame.size.height - 100)];
    // 添加一个重力的行为
    UIGravityBehavior *gravity = [[UIGravityBehavior alloc]initWithItems:@[self.ballView]];
    [self.animator addBehavior:snap];
    [self.animator addBehavior:gravity];
}

效果
模仿重力和吸附效果.gif

3.UIPushBehavior推力,示例:模仿推力 + 碰撞 的行为
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#pragma mark 添加点击手势
- (void)addTapGestrue {
    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(handleTapGestrue:)];
    [self.ballView addGestureRecognizer:tap];
}
- (void)handleTapGestrue:(UITapGestureRecognizer*)gesture{
    // 球的中心点
    CGPoint ballCenter = self.ballView.center;
    // 点击点
    CGPoint tapPoint   = [gesture locationInView:self.view];
    CGFloat offsetX = tapPoint.x - ballCenter.x;
    CGFloat offsetY = tapPoint.y - ballCenter.y;
    CGFloat angle = atan2(offsetY, offsetX);
    CGFloat distance = sqrt(pow(offsetX, 2) + pow(offsetY, 2));
    // 添加推力行为
    UIPushBehavior *push = [[UIPushBehavior alloc]initWithItems:@[self.ballView] mode:UIPushBehaviorModeInstantaneous];
    // 设置角度
    [push setAngle:angle];
    // 设置推力大小
    // 每1个magnigude将会引起100/平方秒的加速度
    [push setMagnitude:distance/100];
    // 添加碰撞行为
    UICollisionBehavior *collision = [[UICollisionBehavior alloc]initWithItems:@[self.ballView]];
    collision.translatesReferenceBoundsIntoBoundary = YES;
    [self.animator addBehavior:push];
    [self.animator addBehavior:collision];
}

效果
模仿推力和碰撞效果.gif

4.UIAttachmentBehavior关联,示例:模仿推力 + 碰撞 的行为
1
2
3
4
5
6
7
8
9
- (void)doAnimation{
    // UIAttachmentBehavior 可以设置和某一个点
    UIAttachmentBehavior *attachment = [[UIAttachmentBehavior alloc]initWithItem:self.blueView attachedToItem:self.ballView];
    // 关联的长度
    [attachment setLength:100];
    UIGravityBehavior *gravity = [[UIGravityBehavior alloc]initWithItems:@[self.blueView]];
    [self.animator addBehavior:attachment];
    [self.animator addBehavior:gravity];
}

效果
模仿关联效果.gif

6.粒子系统

1
2
CAEmitterCell
CAEmitterLayer

7.facebook pop动画

1
2
3
4
5
6
使用之前先讨论以下POP和核心动画的主要区别
CoreAnimation 的动画是加在layer上
CoreAnimation的动画只是表面而已,并没有真正的修改frame等属性值
pop的动画可以添加到任何对象
pop的底层是基于CADisplaylink
pop的动画真正的修改frame等属性值

pop使用:

1
2
3
4
POP默认支持三种动画,但同时也支持自定义动画
POPDecayAnimation 减速动画
POPSpringAnimation 弹簧效果
POPSpringAnimation 飞入效果

期待


  • 如果在阅读过程中遇到 error || new ideas,希望你能 messages 我,我会及时改正谢谢。
  • 点击右上角的 喜欢 和 订阅Rss 按钮,可以收藏本仓库,并在 Demo 更新时收到邮件通知。
❄︎ 本文结束    感谢简阅 ^_^. ❄︎

本文标题:iOS 核心动画—「CALayer图层」

文章作者:寄己的路

原始链接:https://sunyonghui.github.io/iOSNET/CALayer.html

版权声明: 署名-非商业性使用-禁止演绎 4.0 国际 本博客所有文章除特别声明外均为原创,转载务必请「注明出处链接(可点击) - 作者」,并通过E-mail等方式告知,谢谢合作!