核心动画详细阐述
一.系统创建的CALayer
(1) CALayer的简单介绍
-
1.在
iOS
中,你能看的见摸得着的东西基本上都是UIView
,比如一个按钮,一个文本标签,一个文本输入框,一个图标等等,这些都是UIView
, -
2.
UIView
能够显示在屏幕上完全是因为它内部的一个图层 -
3.在创建
UIView
对象时,UIView
内部会自动创建一个图层(即CALayer
对象),通过UIView
的layer
属性可以访问这个层@property(nonatomic,readonly,retain) CALayer *layer;
-
4.当
UIView
需要显示到屏幕上时,会调用drawRect
方法进行绘图,并且会将所有的内容绘制在自己的图层上,绘制完毕后,系统会将图层拷贝到屏幕上,于是进完成了UIView
的显示 -
5.换句话说,
UIView
本身不具备显示的功能,是它内部才有显示功能
(2).CALayer的基本使用
通过操作CALayer
对象,可以方便地调整UIView
的一些外观属性,比如:
-
1.阴影
Opacity
:不透明度(不透明度只要大于1就说明是有阴影的) -
2.圆角大小
-
3.边框宽度和颜色
........... -
还可以给图层添加动画,来实现一些比较炫酷的动画效果
简单的代码展示
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.title = @"早起的太阳";
self.navigationController.navigationBar.barTintColor = [UIColor brownColor];
self.navigationController.navigationBar.titleTextAttributes = @{NSFontAttributeName:[UIFont systemFontOfSize:25],NSForegroundColorAttributeName:[UIColor whiteColor]};
self.view.backgroundColor = [UIColor whiteColor];
UIView *view = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 100, 100)];
view.center = self.view.center;
view.backgroundColor = [UIColor redColor];
[self.view addSubview:view];
/**
* 使一张图片具有阴影的效果
*/
//1.Opacity :不透明度(不透明度只要大于1就说明是有阴影的)
view.layer.shadowOpacity = 1;
//2.阴影的偏移 CGSizeMake(X[正的右偏移,负的左偏移], Y[正的下偏移,负的上偏移]);
//view.layer.shadowOffset = CGSizeMake(10, -10);
//3.设置阴影部分的面积颜色
view.layer.shadowColor = [UIColor yellowColor].CGColor;
//4.阴影的圆角
view.layer.shadowRadius = 20;
//5.view的圆角
view.layer.cornerRadius = 50;
//6.view的边宽
view.layer.borderWidth = 1;
//7.view的边颜色
view.layer.borderColor = [UIColor whiteColor].CGColor;
}
(3)CALayer的图形旋转与缩放:都以layer
图层操作为例(下面以一个button
为例)
主要阐述两点:旋转和缩放两种方法
X,Y,Z-
<1>旋转(这两种方式一样)(建议不要使用KVC快速
缩放
或者旋转
)1.旋转
//button.layer.transform = CATransform3DMakeRotation(M_PI, 1, 0.5, 1);
2.旋转
[button.layer setValue:@M_PI forKeyPath:@"transform.rotation"]; -
<2>缩放(两种方式一样)
1.快速进行图层缩放
//button.layer.transform = CATransform3DMakeScale(0.5, 0.5, 1);
2.缩放
[button.layer setValue:@0.5 forKeyPath:@"transform.scale"];
上面的内容代码 密码: 7u95
二.自己创建的CALayer层
自己创建图层1.创建图层
CALayer *layer = [CALayer layer];
layer.frame = CGRectMake(WIDTH/2-100, 200, 200, 200);
layer.backgroundColor = [UIColor brownColor].CGColor;
2.图层只能添加到图层上面
[self.view.layer addSublayer:layer];
3.给图层添加图片
layer.contents = (id)([UIImage imageNamed:@"girl.jpg"].CGImage);
问题:为什么CALayer图层的颜色后面要加.CGColor
而图片加.CGImage
解释如下:
首先:
(1) CALayer 是定义在QuartzCore框架中的
(2) CGImageRef , CGColorRef 两种数据类型是定义在CoreGraphics中的
(3) UIColor , UIImage 是定义在UIKit 框架中
其次:
(1) QuartzCore框架和CoreGraphics 框架是可以跨平台使用的,在iOS和Mac OS X 上都能使用
(2) 但是UIKit 只能在iOS中使用
所以:为了保证可移植性,QuartzCore
不能使用UIImage
,UIColor
,只能使用CGImageRef
,CGColorRef
三.UIView与CALayer的选择
CALayer继承于NSObject UIView继承于UIResponder(具有响应事件的能力)-
(1) 通过
CALayer
,就能做出跟UIImageView
一样的界面效果 -
(2) 既然
CALayer
和UIView
都能实现相同的显示效果,那如何选择???解释: 对比
CALayer
,UIView
多了一个事件处理的功能,也就是说,CALayer
不能处理用户的触摸事件,而UIView
可以,所以:如果显示出来的东西需要跟用户进行交互的话,用UIView
:如果不需要跟用户进行交互的话,就用UIVIew
或者CALayer
都可以,当然,CALayer
的性能会高一些,因为它多了处理事件的功能,更加轻量级.
**四.介绍一下CALayer的两个重要属性position 和 anchorPoint **
-
@property CGPoint position; 用来设置
CALayer
在父层中的位置,以父层的左上角为原点(0,0):类似frame -
@property CGPoint anchorPoint; 称为"定位点","锚点",决定着CALayer 身上的哪个点会在position 属性所指的位置,以自己的左上角为原点(0,0),它的x,y取值范围都是0~1,默认值为(0.5,0.5):类似bounds
只有图层才有anchorPoint这个属性
具体的显示(锚点默认在中心=center
) 在旋转时:锚点也就是旋转的点
五.隐式动画(手动创建的CALayer才有隐式动画):也就是说自带动画,不需要加animation
-
每一个
UIView
内部都默认连着一个CALayer
,我们可用称这个Layer
为RootLayer
(根层) -
所有的非
Root Layer
,也就是手动创建的CALayer
对象,都存在隐式动画
下面以一个clayer的旋转为例
旋转一般会用到 角度转弧度: 角度/180.0*M_PI
宏定义: #define angle2radion(angle) ((angle) / 180.0 * M_PI)
重点提一下:旋转的中心: layer.anchorPoint = CGPointZero;
anchorPoint的范围是(0~1,0~1);
隐式动画代码 密码: icgj
切记:非根层才能建立隐式动画(也就是自定义的CALayer)
六.时钟的制作
晓效果图如下:
时钟的制作在涉及到旋转的时候我们首先要确定的是它的锚点,其次是position
这里主要是运用了1.隐式动画.2.角度转弧度.3.再就是一些旋转的设置
角度转弧度
#define angle2radion(angle) ((angle) / 180.0 * M_PI)
时钟的代码:里面有中详细的注释 密码: b2c8
在此多数几句:如何获取系统的时间
//获取当前的系统时间
//获取当前日历对象
NSCalendar *calendar =[NSCalendar currentCalendar];
//获取日期的组件,年月日,时分秒
//components: 需要获取日期组件
//formDate:获取哪个日期组件
//经验:以后枚举中有移位运算符(<<),一般来说我们就可以用 | :"并" 的方式
NSDateComponents *dateComponents = [calendar components:NSCalendarUnitSecond | NSCalendarUnitMinute | NSCalendarUnitHour | NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear fromDate:[NSDate date]];
NSLog(@"小时==%ld",dateComponents.hour);
NSLog(@"分钟==%ld",dateComponents.minute);
NSLog(@"秒==%ld",dateComponents.second);
七.核心动画(介绍四个,CABasicAnimation
, CAKeyframeAnimation
, CATransition
, CAAnimationGroup
)
-
<1>.
Core Animation
中文翻译为核心动画,它是一组非常强大的动画处理API
,使用它可以做出非常炫酷的效果,而且往往是事半功倍,也就是说,使用少量的代码就可以实现非常强大的功能. -
<2>.
Core Animation
可以使用在Mac OS X
和iOS
平台. -
<3>.
Core Animation
的动画执行过程都是在后台操作的,不会阻塞主线程. -
<4>.需要注意的是:
Core Animation
是直接作用在CALayer
上的,并非UIView
.在此也说一下UIView
也有动画,其实也是在底层封装了核心动画
- (1)CABasicAnimation创建动画(这里仅仅以一个view的移动为例):只能用于2个值之间
/**
* CABasicAnimation创建核心动画
*/
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
//创建动画
CABasicAnimation *animation = [CABasicAnimation animation];
//描述下修改那个属性产生动画
animation.keyPath = @"position";
//设置值(点转化为对象)
animation.toValue = [NSValue valueWithCGPoint:CGPointMake(arc4random_uniform(500), arc4random_uniform(500))];
//取消动画反弹需要2步
//1.设置动画完成时候不要移除动画
animation.removedOnCompletion = NO;
//2.设置动画执行完成保持最新的效果
animation.fillMode = kCAFillModeForwards;
//添加动画
[_redView.layer addAnimation:animation forKey:nil];
}
如果先个缩放就把上面的值改了就好
animation.keyPath = @"transform.scale";
animation.toValue = @0.5;
如果想做一个心脏的话那么久设置一个一直执行动画,也可以调节动画的时间长短,下面的代码放到一个方法里面就好(可以做心脏的跳动)
二维的放大和缩小
_redVie
就是一个UIImageView
的类的对象
//创建动画
CABasicAnimation *animation = [CABasicAnimation animation];
//描述下修改那个属性产生动画
animation.keyPath = @"transform.scale";
//设置值
animation.toValue = @0.5;
//设置每次动画的时间
animation.duration = 1;
//如果想一直执行动画,很久
animation.repeatCount = MAXFLOAT;//表示最大次数
//取消动画反弹需要2步
//1.设置动画完成时候不要移除动画
animation.removedOnCompletion = NO;
//2.设置动画执行完成保持最新的效果
animation.fillMode = kCAFillModeForwards;
//添加动画
[_redView.layer addAnimation:animation forKey:nil];
当然我们还可以设置旋转
旋转的心脏 animation.keyPath = @"transform.rotation";
animation.toValue = @(M_PI*2);//360旋转
- (2).cakeyframeanimation创建帧动画
1.心脏的抖动
心脏的抖动
代码如下:
//创建动画
CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
//根据路径来走
animation.keyPath = @"transform.rotation";
//角度的设定
animation.values = @[@angle2radion(-5),@angle2radion(5),@angle2radion(-5)];
//不停地循环来抖动
animation.repeatCount = MAXFLOAT;
//添加在图层上的动画
[_heart.layer addAnimation:animation forKey:nil];
当然你也可以设置它的锚点
_heart.layer.anchorPoint = CGPointZero;(以心脏的左上为旋转的点来抖动)
2.让心脏绕着指定轨迹来做运动(用到了贝塞尔曲线
):改变的是位置position
核心代码如下:
//创建动画
CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
//改变的位置
animation.keyPath = @"position";
//动画执行的时间
animation.duration = 2;
//设定转圈的路径
animation.path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(100, 200, 200, 200)].CGPath;
//重复动画
animation.repeatCount = MAXFLOAT;
//添加动画
[_heart.layer addAnimation:animation forKey:nil];
3.心脏根据画出路径来走
首先要建立一个UIView 也就是图层(画画的地方)
效果如下
心脏根据路径来旋转心脏根据路径走的代码 密码: dq43
核心代码如下:
@property(nonatomic,strong) UIBezierPath *path;
/**
* 手指接触屏幕的点
*/
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
//touch
UITouch *touch = [touches anyObject];
//获取手指的触摸点
CGPoint pointCurrent = [touch locationInView:self];
//创建路径
UIBezierPath *path = [UIBezierPath bezierPath];
//记录路径
_path = path;
//设置起点
[path moveToPoint:pointCurrent];
}
/**
* 手指一动的时候的点
*/
-(void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
//touch
UITouch *touch = [touches anyObject];
//获取手指的触摸点
CGPoint pointCurrent = [touch locationInView:self];
//保存每一个路径
[_path addLineToPoint:pointCurrent];
//每次一动就调用画画
[self setNeedsDisplay];
}
/**
* 画线
*/
-(void)drawRect:(CGRect)rect
{
[_path stroke];
}
/**
* 手指抬起来的时候
*/
-(void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
//创建动画
CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
animation.keyPath = @"position";
animation.duration = 2;
animation.path = _path.CGPath;
animation.repeatCount = MAXFLOAT;
//添加动画
[[[self.subviews firstObject] layer] addAnimation:animation forKey:nil];
}
添加的动画
添加动画
[[[self.subviews firstObject] layer] addAnimation:animation forKey:nil];
注意:我再重复一次,动画式添加在图层上的
- (3).转场动画CATransition
转场动画和转场代码要写在一起:(切记)
效果图如下:
转场动画核心代码如下:(转场动画和转场代码要写在一起)
/**
* 在触摸时的变化
*/
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
转场代码
if(i==4)
{
i = 1;
}
NSString *imageName = [NSString stringWithFormat:@"%d.jpg",i];
_imageView.image = [UIImage imageNamed:imageName];
i++;
添加转场动画
CATransition *animation = [CATransition animation];
animation.type = @"cube";
animation.duration = 2;
[_imageView.layer addAnimation:animation forKey:nil];
}
如果想看其他的效果:可以更改下面的值:
animation.type = @"cube";
转场过渡效果
转场动画代码 密码: ffmy
- (4).动画组(之前的动画只能缩放或者旋转,动画组可以实现旋转和缩放同时进行)
效果如下:
动画组核心的代码如下:(也就是一个group来包含很多的动画组合起来的)
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
//同时缩放,平移,旋转
CAAnimationGroup *group = [CAAnimationGroup animation];
group.duration = 2;
CABasicAnimation *scale = [CABasicAnimation animation];
scale.keyPath = @"transform.scale";
scale.toValue = @0.5;
CABasicAnimation *rotation = [CABasicAnimation animation];
rotation.keyPath = @"transform.rotation";
rotation.toValue = @(arc4random_uniform(M_PI));
CABasicAnimation *position = [CABasicAnimation animation];
position.keyPath = @"position";
position.toValue = [NSValue valueWithCGPoint:CGPointMake(arc4random_uniform(200)+100, arc4random_uniform(400)+100)];
group.animations = @[scale,rotation,position];
//取消动画反弹需要2步
//1.设置动画完成时候不要移除动画
group.removedOnCompletion = NO;
//2.设置动画执行完成保持最新的效果
group.fillMode = kCAFillModeForwards;
[_imageView.layer addAnimation:group forKey:nil];
}
动画组代码 密码: 4yiq
再重复一次:核心动画式加在图层上的
八.UIView与核心动画的区别
注意:
1.核心动画一切都是假象,并不会真实的改变图层的属性值.如果以后做动画的时候,不需要与用户交互,通常用核心动画(转场)
原因:当我们改变一个视图的position的时候,它的position并没有发生改变,大家可以打印一下看看(打印看看)
NSLog(@"%@",NSStringFromCGPoint(_girl.layer.position));
2.UIView动画必须通过修改属性的真实值,才有动画效果
[UIView animateWithDuration:1 animations:^{
} completion:^(BOOL finished) {
}];