##一、CALayer概述git
CALayer
类在概念上和UIView
相似,一样也是一些被层级关系 树管理的矩形块,一样也能够包含一些内容(像图片,文本或者背景色),管理子图层的位置。它们有一些⽅法和属性用来作动画和变换。 CALayer是CoreAnimation部分的内容,CALayer的概念相似于photoshop中层的概念,每一个UIView都有一个根CALayer,每一个CALayer又能够添加子CALayer,从结构上来看CALayer是一种树形结构,UIView的绘制工做都交由CALayer完成。github
和UIView最大的不一样是CALayer
不处理⽤用户的交互。由于CALayer
并不清楚具体的响应链(iOS经过视图层级关系用来传送触摸事件的机制),因而它并不可以响应事件,即便它提供了一些方法来判断是否一个触点在图层的范围以内。数据结构
##二、平行的层级关系 每个UIview
都有一个CALayer
实例的图层属性,也就是所谓的backing layer,视图的职责就是建立并管理这个图层,以确保当子视图在层级关系中添加或者被移除的时候,他们关联的图层也一样对应在层级关系树当中有相同的操做。 实际上,这些背后关联的图层才是真正用来在屏幕上显示和作画,UIView
仅仅是对CALayer
的一个封装,而后提供了处理触摸的具体功能,以及CoreAnimation
高级接口。可是为何iOS要基于UIView和CALayer提供两个平行的层级关系呢?为何不⽤一个简单的层级来处理全部事情呢?缘由在于要作职责分离,这样也能避免不少重复代码。在iOS和Mac OS两个平台上,事件和⽤户交互有不少地方的不一样,基于多点触控的用户界⾯和基于⿏标键盘有着本质的区别,这就是为何iOS有UIKit和UIView,可是Mac OS有AppKit和NSView的缘由。他们功能上很类似,可是在实现上有着显著的区别。ide
实际上,这里并非两个层级关系,而是四个,每个都扮演不一样的角色,除了视图层级和图层树以外,还存在呈现树和渲染树.函数
呈现树:呈现树包含了当前动画发生时候将要显示的值,例如你要给图层背景颜色设置新的值的时候,它会当即修改图层树里面相应的值。可是在呈现树里面背景颜色值在将要显示给用户的时候才被更新为新值。oop
渲染树:渲染树是私有的,你没法访问到,渲染树在渲染图层的时候使用呈现树的值,为了避免阻塞主线程,渲染的过程是在单独的进程或线程中进行的,因此你会发现Animation的动画并不会阻塞主线程。学习
前⾯面已经讲过,UIView是对CALayer的封装,那为何咱们还要学习CALayer呢?由于UIView封装的 API在有些状况下并不能知足咱们的需求,好比:字体
1.阴影,圆⾓角,边框 2.3D变换 3.⾮矩形范围 4.遮罩 5.⾮线性动画
##三、CALayer经常使用属性 咱们能够进入CALayer.h中查看CALayer支持的属性,其中注解中标注Animation的属性表示支持隐式动画,当这些属性的值改变时系统自带了平滑过渡的动画效果(非根Layer支持)动画
下表列出了CALayer经常使用的属性:线程
属性 | 说明 | 是否支持隐式动画 |
---|---|---|
anchorPoint | 和中心点position重合的一个点,称为“锚点”,锚点的描述是相对于x、y位置比例而言的默认在图像中心点(0.5,0.5)的位置 | 是 |
backgroundColor | 图层背景颜色 | 是 |
borderColor | 边框颜色 | 是 |
borderWidth | 边框宽度 | 是 |
bounds | 图层大小 | 是 |
contents | 图层显示内容,例如能够将图片做为图层内容显示 | 是 |
contentsRect | 图层显示内容,例如能够将图片做为图层内容显示 | 是 |
contentsRect | 图层显示内容的大小和位置 | 是 |
cornerRadius | 圆角半径 | 是 |
doubleSided | 图层背面是否显示,默认为YES | 否 |
frame | 图层大小和位置,不支持隐式动画,因此CALayer中不多使用frame,一般使用bounds和position代替 | 否 |
zPosition | 图层的重叠顺序 | 是 |
hidden | 是否隐藏 | 是 |
mask | 图层蒙版 | 是 |
maskToBounds | 是否剪切超出父图层边界的部分,默认为NO | 是 |
opacity | 透明度 ,相似于UIView的alpha | 是 |
position | 图层中心点位置,相似于UIView的center | 是 |
shadowColor | 阴影颜色 | 是 |
shadowOffset | 阴影偏移量 | 是 |
shadowOpacity | 阴影透明度,注意默认为0,若是设置阴影必须设置此属性 | 是 |
shadowPath | 阴影的形状 | 是 |
shadowRadius | 阴影模糊半径 | 是 |
sublayers | 设置多个子图层 | 是 |
sublayerTransform | 子图层形变 | 是 |
transform | 图层形变 | 是 |
建立layer并添加到根layer上:
CALayer *layer = [[CALayer alloc] init]; layer.frame = CGRectMake(10, 320, 330, 200); layer.backgroundColor = [UIColor blueColor].CGColor; // 将图层添加到父图层 [self.view.layer addSublayer:layer];
填充图片内容,须要将 UIImage 桥接(__bridge)到CGImage:
layer.contents = (__bridge id _Nullable)([UIImage imageNamed:@"image4.jpg"].CGImage);
使用layer CATextLayer 子类填充文字:
CATextLayer *textLayer = [[CATextLayer alloc] init]; textLayer.frame = CGRectMake(10, 550, 300, 30); textLayer.string = @"这是layer填充的文字内容"; //字体颜色 textLayer.foregroundColor = [UIColor blackColor].CGColor; textLayer.backgroundColor = [UIColor redColor].CGColor; textLayer.font = (__bridge CFTypeRef _Nullable)([UIFont systemFontOfSize:20 weight:500]); textLayer.fontSize = 20; textLayer.alignmentMode = @"center"; // textLayer.truncationMode = @"middle"; [self.view.layer addSublayer:textLayer];
##四、CALayer图层绘制
CALayer *layer = [CALayer layer]; // layer.frame = CGRectMake(0, 100, 350, 200); layer.backgroundColor = [UIColor orangeColor].CGColor; // 一、bounds: 尺寸 layer.bounds = CGRectMake(0, 0, 220, 220); // 二、position: 定位点 layer.position = self.view.center; // 三、锚点、支点:决定layer上的哪一个点在 position 点上,默认(0.5, 0.5),范围:(0,0) ~ (1,1) layer.anchorPoint = CGPointMake(0.5, 0.5); // 四、z方向的层级 layer.zPosition = 2; // 五、设置圆角:为直径的一半时会成圆形 layer.cornerRadius = 110; // 六、填充内容 layer.contents = (__bridge id _Nullable)([UIImage imageNamed:@"image1.jpg"].CGImage); //设置背景颜色 layer.backgroundColor = [UIColor orangeColor].CGColor; // 七、是否能够裁剪多余的图层 layer.masksToBounds = YES; // 八、设置边框宽度和颜色 //layer.borderWidth = 5; //layer.borderColor = [UIColor lightGrayColor].CGColor; [self.view.layer addSublayer:layer]; }
实现效果:
须要注意的是上面代码中绘制图片圆形裁切效果时若是不设置masksToBounds是没法显示圆形,可是对于其余图形却没有这个限制。缘由就是当绘制一张图片到图层上的时候会从新建立一个图层添加到当前图层,这样一来若是设置了圆角以后虽然底图层有圆角效果,可是子图层仍是矩形,只有设置了masksToBounds为YES让子图层按底图层剪切才能显示圆角效果。
若是设置了masksToBounds=YES
以后确实能够显示图片圆角效果,但遗憾的是设置了这个属性以后就没法设置阴影效果。由于masksToBounds=YES
就意味着外边框不能显示,而阴影偏偏做为外边框绘制的,这样两个设置就产生了矛盾。要解决这个问题不妨换个思路:使用两个大小同样的图层,下面的图层负责绘制阴影,上面的图层用来显示图片。
//阴影图层 CALayer *layerShadow = [[CALayer alloc]init]; layerShadow.bounds = CGRectMake(0, 0, 220, 220); layerShadow.position= self.view.center; layerShadow.cornerRadius = 110; //阴影颜色 layerShadow.shadowColor = [UIColor grayColor].CGColor; //阴影偏移量 layerShadow.shadowOffset = CGSizeMake(5, 1); //阴影透明度 layerShadow.shadowOpacity = 1; layerShadow.borderColor = [UIColor whiteColor].CGColor; layerShadow.borderWidth = 5; [self.view.layer addSublayer:layerShadow]; }
实现效果:
##五、CALayer3D变换
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { UITouch *touch=[touches anyObject]; CGFloat width=layer.bounds.size.width; if (width==Width) { width=Width*2; // 组合 CATransform3D CATransform3D transform_01 = CATransform3DScale(layer.transform, 1, 1, 1); CATransform3D transform_02 = CATransform3DRotate(layer.transform, M_PI/6, 0, 0, 1); layer.transform = CATransform3DConcat(transform_01, transform_02); layer.cornerRadius = width/2; layer.backgroundColor = [UIColor blueColor].CGColor; }else{ width=Width; layer.cornerRadius = 0; layer.backgroundColor = [UIColor orangeColor].CGColor; } layer.bounds=CGRectMake(0, 0, width, width); layer.position=[touch locationInView:self.view]; } @end
实现效果:
1:CATransform3DMakeRotation(CGFloat angle, <#CGFloat x#>, <#CGFloat y#>, <#CGFloat z#>)
若是x=1,y=0,z=0则绕x轴旋转angle角度
若是x=0,y=1,z=0则绕y轴旋转angle角度
若是x=0,y=0,z=1则绕z轴旋转angle角度
若是x=1,y=1,z=0则绕x轴和y轴夹角旋转angle角度
若是x=1,y=1,z=1则绕3轴夹角旋转angle角度
上诉的旋转中心都是layer的锚点(anchorPoint)
2:CATransform3DRotate(<#CATransform3D t#>, <#CGFloat angle#>, <#CGFloat x#>, <#CGFloat y#>, <#CGFloat z#>)
功能与上一函数相似,但能够叠加一个CATransform3D效果
3:CATransform3DMakeScale(<#CGFloat sx#>, <#CGFloat sy#>, <#CGFloat sz#>)
用于缩放,三个参数是x轴,Y轴,z轴上的缩放程度,缩放中心是layer的锚点
4:CATransform3DScale(<#CATransform3D t#>, <#CGFloat sx#>, <#CGFloat sy#>, <#CGFloat sz#>)
功能与上一函数相似,但能够叠加一个CATransform3D效果
5:CATransform3DMakeTranslation(CGFloat tx, <#CGFloat ty#>, <#CGFloat tz#>)
用于平移
6:CATransform3DTranslate(<#CATransform3D t#>, <#CGFloat tx#>, <#CGFloat ty#>, <#CGFloat tz#>)
用于移动
7:CATransform3DConcat(CATransform3D a, <#CATransform3D b#>)
将两个CATransform3D效果叠加起来
咱们也能够经过KVC更方便的设置transform的属性
[layer setValue:@M_PI forKeyPath:@"transform.rotation.x"];
能够经过KVC设置如下与transform相关的属性
transform.rotation.x
``transform.rotation.y`
transform.rotation.z
transform.scale.x
transform.scale.y
transform.scale.z
transform.translation.x
transform.translation.y
transform.translation.z
##六、CALayer事务 CALayer中"Animatable"属性变化都在CATrasaction的管理内,以前提到的属性支持隐式动画是指在某次Runroop中修改"Animatable"时,若是没有设置事务,则会自动建立一个CATransaction,并在当前线程的下一个RunLoop中commit这个CATransaction。
事务能够被嵌套,容许你禁用部分动画的行为或者在属性被修改的时候产生 的动画使用不一样的时间。仅当最外层的事务被提交的时候,动画才会发生。
事务开启:[CATransaction begin]
禁止动画效果:[CATransaction setDisableActions:YES];
事务提交:[CATransaction commit]
咱们能够经过事务控制动画的时长,甚至禁止动画效果,还能够设置completionBlock,当当前CATransaction的全部动画执行结束后,,completionBlock会被调用
##Demo下载地址: https://github.com/fuxinto/HfxDemo