iOS学习——核心动画之Layer基础
1、CALayer是什么?
CALayer我们又称它叫做层。在每个UIView内部都有一个layer这样一个属性,UIView之所以能够显示,就是因为它里面有这个layer才具有显示的功能。我们可以通过操作CALayer对象,可以很方便地调整UIView的一些外观属性,可以给UIView设置阴影,圆角,边框等等...
2、如何操作layer改变UIView外观?
2.1 设置阴影
//默认图层是有阴影的, 只不过,是透明的 _RedView.layer.shadowOpacity = 1; //设置阴影的圆角 _RedView.layer.shadowRadius =10; //设置阴影的颜色,把UIKit转换成CoreGraphics框架,用.CG开头 _RedView.layer.shadowColor = [UIColor blueColor].CGColor;
2.2.设置边框
//设置图层边框,在图层中使用CoreGraphics的CGColorRef _RedView.layer.borderColor = [UIColor whiteColor].CGColor; _RedView.layer.borderWidth = 2;
2.3.设置圆角
//图层的圆角半径,圆角半径为宽度的一半, 就是一个圆 _RedView.layer.cornerRadius = 50;
3、如何操作layer改变UIImageView的外观?
//设置图形边框 _imageView.layer.borderWidth = 2; _imageView.layer.borderColor = [UIColor whiteColor].CGColor; //设置图片的圆角半径,必须要进行第二步的裁剪,超出裁剪区域的部分全部裁剪掉 _imageView.layer.cornerRadius = 50; _imageView.layer.masksToBounds = YES;
注意:设置图片的圆角时,除了设置圆角半径,还必须要进行第二步的裁剪,设置masksToBounds为yes。这是因为UIImageView当中Image并不是直接添加在层上面的,这是添加在layer当中的contents里。UIImageView中是UIView的主layer上添加了一个次layer(用来绘制contents),我们设置边框的是主layer,但是次layer在上变,不会有任何的影响,所以当我们调用切割语句的时候,超出边框意外的都被切割了!!
我们设置层的所有属性它只作用在层上面,对contents里面的东西并不起作用,所以如果我们不进行裁剪,我们是看不到图片的圆角效果的。想要让图片有圆角的效果,就必须把masksToBounds这个属性设为YES,当设为YES,把就会把超过根层以外的东西都给裁剪掉。
4、layer的 CATransform3D属性变换
UIView和Layer都有transform属性,但是他们的所属有区别,类型也有区别
1.picView.transform是二维的属性,是CGAffineTransform类型
2.picView.layer.transform是layer级别的三维属性,是CATransform3D类型的,当然也可以做二维的事情,只有旋转的时候才可以看出3D的效果.
//旋转 x,y,z 分别代表x,y,z轴. CATransform3DMakeRotation(M_PI, 1, 0, 0); //平移 CATransform3DMakeTranslation(x,y,z) //缩放 CATransform3DMakeScale(x,y,z);
属性设置有三种方法
//1.直接使用基本的三维赋值方法 picView.layer.transform = CATransform3DMakeScale(1, 2.5, 0); //2.使用KVC将CATransform3DMakeScale生成的对象给layer NSValue *value = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(M_PI, 1, 0, 0)]; [picView.layer setValue:value forKeyPath:@"transform.scale"]; //3.使用快捷方法设置属性 [picView.layer setValue:@5 forKeyPath: "transform.scale.y"];
什么时候用KVC?
当需要做一些快速缩放,平移,二维的旋转时用KVC。后面forKeyPath属性值不是乱写的,苹果文档当中给了相关的属性.
比如: [_imageView.layer setValue:@0.5 forKeyPath:@"transform.scale"];
5、如何自定义Layer ?
自定义CALayer的方式创建UIView的方式非常相似。
//创建 CALayer *layer = [CALayer layer]; //设置尺寸和位置 layer.frame = CGRectMake(50, 50, 100, 100); //设置背景 layer.backgroundColor = [UIColor redColor].CGColor; //给layer设置图片. layer.contents = (id)[UIImage imageNamed:@"image001"].CGImage; //加载绘制 [self.view.layer addSublayer:layer];
6、为什么要使用CGImageRef、CGColorRef?
为了保证可移植性,QuartzCore不能使用UIImage、UIColor,只能使用CGImageRef、CGColorRef
7、UIView和CALayer都能够显示东西,该怎样选择?
在明白要怎么选择之前,我们先了解一下UIView和layer的不同点:
-
- 在iOS中看的见,摸得着的都是UIView,例如一个按钮,UITextField,UILable等等,都是UIView
- UIView之所以能够显示在屏幕上,是试音UIView中有一个图层
- 在创建UIView的时候,系统会自动创建一个CALayer在其中,用于显示东西,可以通过view.layer来去获取图层属性
- 当UIView要去显示的时候,先去调用
drawRect
方法,将要绘制的东西绘制到图层上,然后拷贝图层,完成了UIView的显示 - UIView只有交互的功能,没有显示的功能
- CALayer只要显示的功能,没有交互的功能
- UIView可以做一些简单的动画,例如:平移,拉伸,旋转
- 一些比较高端的动画,都是直接操作CALayer的,可以制作3D动画
- 使用CALayer,可以直接操作显示的东西,例如阴影,圆角,边框等
所以,对比CALayer,UIView多了一个事件处理的功能。也就是说,CALayer不能处理用户的触摸事件,而UIView可以。
如果显示出来的东西需要跟用户进行交互的话,用UIView;
如果不需要跟用户进行交互,用UIView或者CALayer都可以,CALayer的性能会高一些,因为它少了事件处理的功能,更加轻量级。
8、position和anchorPoint?
position和anchorPoint是CAlayer的两个属性,我们以前修改一个控件的位置都是通过Frame的方式进行修改。现在CALayer则是通过position和anchorPoint属性也能够修改控件的位置,
这两个属性是配合使用的。
position:它是用来设置当前的layer在父控件当中的位置的,默认它的坐标原点,以父控件的左上角为(0.0)点。
anchorPoint:锚点,就是把锚点定到position所指的位置。它是决点CALayer身上哪一个点会在position属性所指的位置,anchorPoint它是以当前的layer左上角为原点(0.0),它的取值范围是0~1,它的默认在中间也就是(0.5,0.5)的位置。
两者结合使用,想要修改某个控件的位置,我们可以设置它的position点。设置完毕后,layer身上的anchorPoint会自动定到position所在的位置。
//下面两行代码就是设置views的 正中间 坐标(200,200) _views.layer.position = CGPointMake(200, 200); _views.layer.anchorPoint = CGPointMake(0.5, 0.5); //下面两行代码就是设置views的 左上角 坐标(200,200) _views.layer.position = CGPointMake(200, 200); _views.layer.anchorPoint = CGPointMake(0, 0); //下面两行代码就是设置views的 右下角 坐标(200,200) _views.layer.position = CGPointMake(200, 200); _views.layer.anchorPoint = CGPointMake(1, 1);
9、隐式动画
9.1 什么是隐式动画?
了解什么是隐式动画,要先了解是什么根层和非根层:
-
- 根层:UIView内部自动关联着的那个layer我们称它是根层.
- 非根层:自己手动创建的层,称为非根层.
隐式动画就是当对非根层的部分属性进行修改时, 它会自动的产生一些动画的效果,我们称这个默认产生的动画为隐式动画.
9.2 如何取消隐式动画?
首先要了解动画底层是怎么做的,动画的底层是包装成一个事务来进行的。
什么是事务? 很多操作绑定在一起,当这些操作执行完毕后,才去执行下一个操作.
//开启事务 [CATransaction begin]; //设置事务没有动画 [CATransaction setDisableActions:YES]; //设置动画执行的时长 [CATransaction setAnimationDuration:2]; //提交事务 [CATransaction commit];
10、时钟效果
10.1 搭建界面
界面上时针、分针、秒针不需要与用户进行交互,所以都可以使用layer方式来做,具体时间可以用用一张圆形图片来显示,然后在这个imageView的layer中进行时针、分针和秒针的绘制。
做之前要观察时针在做什么效果。是根据当前的时间,绕着表盘的中心点进行旋转.
要了解一个非常重要的知识点,无论是旋转,缩放它都是绕着锚点进行的。要想让时针、分针、称针显示的中间,还要绕着中心点进行旋转,那就要设置它的position和anchorPoint两个属性.
//添加秒针 - (void)addSecond{ //创建秒针 CALayer *layer = [CALayer layer]; _secondL = layer; //设置宽高 layer.bounds = CGRectMake(0, 0, 1, 80); //设置锚点为秒针的 x轴中心,y轴最右端,该锚点的位置是时钟图片的正中心 layer.anchorPoint = CGPointMake(0.5, 1); layer.position = CGPointMake(_clockView.bounds.size.width * 0.5, _clockView.bounds.size.height * 0.5); //设置秒针的颜色 layer.backgroundColor = [UIColor redColor].CGColor; //将秒针的layer添加到时钟图片的layer中 [_clockView.layer addSublayer:layer]; } //时针、分针的添加方式类似,只是设置的宽高有点区别,不再贴出来
10.2 让秒针开始旋转.
//角度转换成弧度 #define angle2Rad(angle) ((angle) / 180.0 * M_PI) //每一秒 秒针 旋转6度 #define perSecondA 6 //每一分 分针 旋转 #define perMinA 6 //每一小时 时针 旋转30 #define perHourA 30 //第一分钟 时针 旋转0.5 #define perMinHour 0.5 //每一秒调用一次 - (void)timeChage{ NSCalendar *calendar = [NSCalendar currentCalendar]; //components日历单元:年,月,日,时,分,秒 //fromDate:从哪个时间开始取 NSDateComponents *cmp = [calendar components:NSCalendarUnitSecond | NSCalendarUnitMinute | NSCalendarUnitHour fromDate:[NSDate date]]; //获取当前时间的时 分 秒 NSInteger curSecond = cmp.second; NSInteger curMinute = cmp.minute; NSInteger curHour = cmp.hour; //秒针 旋转多少度. 当前的秒数乘以每秒转多少度. CGFloat angle = curSecond * perSecondA; self.secondL.transform = CATransform3DMakeRotation(angle2Rad(angle), 0, 0, 1); //分针 旋转多少度. 当前的分钟乘以每分转多少度. CGFloat minuteA = curMinute * perMinA; self.minuteL.transform = CATransform3DMakeRotation(angle2Rad(minuteA), 0, 0, 1); //时针旋转的度数应该是 多少小时对应的度数+分钟对应的时针旋转的度数 CGFloat hourA = curHour * perHourA + curMinute * perMinHour; self.hourL.transform = CATransform3DMakeRotation(angle2Rad(hourA), 0, 0, 1); }
10.3 将布局和旋转进行组合
每过一秒,我们的秒针就需要变化位置,所以我们我们需要设置一个定时器,在开始时每秒执行一次旋转布局绘制。
- (void)viewDidLoad { [super viewDidLoad]; //添加时针 分针 秒针 [self addHour]; [self addMinue]; [self addSecond]; //添加定时器,每秒进行绘制 [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timeChage) userInfo:nil repeats:YES]; //绘制 [self timeChage]; }