本文主要记录CALayer的基本操作,UIView和CALayer的选择,CATransform3D。
一.CALayer
1.简介
iOS中的事件传递和响应中记录过:遵循单一原则,UIView提供内容,处理触摸等事件,参与响应链;CALayer负责显示内容contents。
当UIView需要显示到屏幕上时,会调用drawRect:方法进行绘图,并将内容绘制在自己的图层上,绘图完成后系统将图层拷贝到屏幕上进行UIView的显示;
2.CALayer的疑惑
为什么CALayer上面的颜色、图片是CGColorRef
、CGImageRef
类型的?
首先
->CALayer
是定义在QuartzCore框架中的;
->CGColorRef
、CGImageRef
两种数据类型是定义在CoreGraphics中的;
->UIImage
和UIColor
是定义在UIKit框架中的;其次
->QuartzCore、CoreGraphics这两个框架可以跨平台在iOS和Mac OSX上都能使用;
->UIKit只能在iOS中使用所以,为了保证可移至性,QuartzCore不能使用
UIImage
和UIColor
3.CALayer使用
其实我们一直都在用layer的这些属性,例如通过操作CALayer对象,可以很方便的调整UIView的一些外观属性:阴影、圆角、边框宽度和颜色、给图层添加动画效果...
3.1 CALayer基本使用
削圆的时候,非UIImageView设置cornerRadius即可;
UIImageView的image是添加在imageView-->layer-->contents上面的;只是设置圆角无法看到效果,要把多余部分剪裁掉才可以,所以需要设置.layer.masksToBounds = YES;
//设置边框(是添加到redView的里面:把边框改大可查看)
self.redView.layer.borderColor = [UIColor blueColor].CGColor;
self.redView.layer.borderWidth = 10;
//设置阴影
self.redView.layer.shadowOpacity = 1;
self.redView.layer.shadowOffset = CGSizeMake(10, 10);
self.redView.layer.shadowColor = [UIColor greenColor].CGColor;
//设置圆角
self.redView.layer.cornerRadius = 50;
//UIImageView削圆
//设置圆角
self.imageView.layer.cornerRadius = 50;
//超过根层以为的内容裁减掉(造成离屏渲染,CPU的工作交给了GPU,消耗性能)--->所以要使用Quartz 2D来剪裁
self.imageView.layer.masksToBounds = YES;
3.2 CALayer的属性--transform(CATransform3D)
使用layer的transform属性可以进行旋转、平移、缩放;和UIView的transform相比:
UIView的transform:CGAffineTransform类型
layer的transform:CATransform3D类型(是3D空间的,比UIView的transform可做的多一些,比如对Z轴操作就需要layer的transform)
3.2.1 平移、旋转、缩放
//1.旋转
//angle:旋转角度;X,Y,Z里面绕哪个轴旋转就为1;x,y都为1,就绕x,y中间的中轴旋转
self.imageView.layer.transform = CATransform3DMakeRotation(M_PI, 1, 0, 0);
//2.平移
//z:层级;值大的在上面
self.imageView.layer.transform = CATransform3DMakeTranslation(50, 50, 1000);
//不带make的,是相对于某一个来进行操作的;带make就是相对于原点操作的
self.imageView.layer.transform = CATransform3DTranslate(self.imageView.layer.transform, 50, 50, 1000);
//3.缩放
self.imageView.layer.transform = CATransform3DScale( self.imageView.layer.transform, 1.5, 1.5, 1);
3.2.2 通过KVC只对某一个值操作
通过kvc使用场景,用来做快速变性操作,只做一个值的操作
NSValue * value = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(M_PI, 1, 0, 0)];
[self.imageView.layer setValue:value forKey:@"transform"];
eg:只让x放大
[self.imageView.layer setValue:@(2) forKeyPath:@"transform.scale.x"];
要设置的key参考如下表:
3.2.3.自定义CALayer
CALayer * layer = [CALayer layer];
layer.frame = CGRectMake(50, 50, 100, 100);
layer.backgroundColor = [UIColor redColor].CGColor;
layer.contents = (id)[UIImage imageNamed:@"1.png"].CGImage;
[self.view.layer addSublayer:layer];
也可以对自定义layer直接削圆、描边等操作。eg:
//--添加头像外轮廓
CALayer *avatarBorder = [CALayer layer];
avatarBorder.frame = _avatarView.bounds;
avatarBorder.borderWidth = CGFloatFromPixel(1);
avatarBorder.borderColor = [UIColor colorWithWhite:0.000 alpha:0.090].CGColor;
avatarBorder.cornerRadius = _avatarView.height / 2;
avatarBorder.shouldRasterize = YES;
avatarBorder.rasterizationScale = kScreenScale;
[_avatarView.layer addSublayer:avatarBorder];
4.CALayer和UIView的选择
CALayer做到的UIView也可以做,但是相比下来,CALayer的性能更高,更加轻量级。
前面介绍CAlayer的基本操作,UIView也可以实现,那什么时候用CALayer、什么时候用UIView呢?
->不需要事件交互的使用CALayer;需要使用的使用UIView;
->但是为了可扩展性,一般需要使用UIView,以防目前不需要交互,后期需要交互;
二. CALayer的属性:position、anchorposition
CALayer的有两个很重要的属性:position、anchorposition;
1.position
- 用来设置CALyer在附赠中的位置
- 父层的左上角为原点(0,0)
2. anchorPosition(定位点、锚点)
- anchorposition即layer的哪个点和position重合
- 以自己的左上角为原点(0,0)
- 它x、y取值范围是0-1,默认值为(0.5,0.5)
如下图,假如红色图层以下几个点分别为锚点。
第(1)个点为锚点时,(0,0)是锚点
第(2)个点为锚点时,(0.5,0.5)是锚点
第(3)个点为锚点时,(1,1)是锚点
第(4)个点为锚点时,(0.5,1)是锚点
3.position和anchorPosition
添加橘色图层到蓝色图层上面;
橘色图层显示到什么位置,由position属性决定;
(1)假设橘色图层的position是(100,100);橘色图层的anchorPosition是(0,0);即橘色图层的(0,0)点和蓝色图层的(100,100)重合
(2)假设橘色图层的position是(100,100);橘色图层的anchorPosition是(0.5,0.5);即橘色图层的(0.5,0.5)点和蓝色图层的(100,100)重合
(3)假设橘色图层的position是(100,100);橘色图层的anchorPosition是(1,1);即橘色图层的(1,1)点和蓝色图层的(100,100)重合
4.代码示例
自定义layer,anchorPosition默认为(0.5,0.5);
设置layer的position为 self.view.center;
相当于是让layer的center(0.5,0.5)和view的center重合;
从而可以得到,当anchorPosition为(0.5,0.5)时,UIView的center就是内部layer的position。
CALayer * layer = [CALayer layer];
layer.backgroundColor = [UIColor redColor].CGColor;
layer.bounds = CGRectMake(0, 0, 100, 100);
layer.position = self.view.center;
[self.view.layer addSublayer:layer];
5.隐式动画
每个UIView关联的CALayer,称为Root Layer(根层);
所有手动创建的layer,即非根层,都存在着隐式动画;
什么是隐式动画?
当对非根层layer的Animatable Properties(可动画属性)进行修改时,会默认产生一些动画。
Animatable Properties:
bunds:修改这个属性会产生缩放动画;
backgroundColor:修改会产生背景色渐变动画;
position:修改会产生平移动画
取消隐式动画
隐示动画封装了一个事务;我们可以使用CATransaction,把要取消的动画放到事务里面。
如下:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
//取消隐式动画
[CATransaction setDisableActions:YES];
self.layer.position = self.view.center;
//捆绑哪些操作,如果不写的话,系统会自动给所有动画加上
[CATransaction commit];
self.layer.cornerRadius = 30;
}
6.显示动画
既然有隐式动画,那肯定有显示动画。显示动画就是我们平时所指的Core Animation。比如属性动画、动画组、过渡等。详细可以看Core Animation
三. CALayer的属性:contentGravity
这个属性类似于的contentMode,目的是为了决定内容在图层的边界中怎么对齐。
view.contentMode = UIViewContentModeScaleAspectFit;
如下,把一个正方形的image添加到蓝色的长方形layer里面(左图是对齐前,右图是对齐后):
//创建layer
CALayer *blueLayer = [CALayer layer];
blueLayer.frame = CGRectMake(50.0f, 50.0f, 200.0f, 300.0f);
blueLayer.backgroundColor = [UIColor blueColor].CGColor;
//添加image到contents
UIImage *image = [UIImage imageNamed:@"1"];
blueLayer.contents = (id)image.CGImage;
//设置对齐方式
blueLayer.contentsGravity = kCAGravityResizeAspect;
[self.view.layer addSublayer:blueLayer];
四. CALayer的属性:contentsScale
如果我们把contentsGravity设置为kCAGravityCenter(这个值并不会拉伸图片),图片显示如下,很明显图片超过了layer很多,也虚化了。
//创建layer
CALayer *blueLayer = [CALayer layer];
blueLayer.frame = CGRectMake(50.0f, 50.0f, 200.0f, 300.0f);
blueLayer.backgroundColor = [UIColor blueColor].CGColor;
//添加image到contents
UIImage *image = [UIImage imageNamed:@"1"];
blueLayer.contents = (id)image.CGImage;
//设置显示为中间对齐
blueLayer.contentsGravity = kCAGravityCenter;
[self.view.layer addSublayer:blueLayer];
- 这个时候需要contentsScale,contentsScale默认为1。
- contentsScale属于支持高分辨率(又称Hi-DPI或Retina)屏幕机制的一部分。用来判断在绘制图层的时候应该为创建的空间大小。
- 如果contentsScale设置为1.0,将会以每个点1个像素绘制图片,如果设置为2.0;则会以每个点2个像素绘制图片,这就是我们熟知的Retina屏幕。
换句话说:
contentScale:决定着绘制图层的时候,1个点对应几个像素。例如iphone6、7、8,1个点对应2个像素。那么contentScale应该就为2。iphone6p、7p,1个点对应3个像素,contentScale应该就为2。
通过[UISCreen mainScreen].scale
可获取当前屏幕每个点需要对应几个像素。
刚刚没有设置contentScale,默认1个点对应1个像素,所以虚化了。现在修改后,效果如下:
CALayer *blueLayer = [CALayer layer];
blueLayer.frame = CGRectMake(50.0f, 50.0f, 200.0f, 300.0f);
blueLayer.backgroundColor = [UIColor blueColor].CGColor;
UIImage *image = [UIImage imageNamed:@"1"];
blueLayer.contents = (id)image.CGImage;
blueLayer.contentsGravity = kCAGravityCenter;
//设置contentScale
blueLayer.contentsScale = [UIScreen mainScreen].scale;
[self.view.layer addSublayer:blueLayer];