一.简介:
CALayer类在概念上和UIView类似,同样也是一些被层级关系树管理的矩形块,同样也可以包含一些内容(像图片,文本或者背景色),管理子图层的位置。它们有一些方法和属性用来做动画和变换。和UIView最大的不同是CALayer不处理用户的交互。
CALayer只是UIView 的一个基本架构,layer使绘制和动画视图内容和保持高帧速率的内容变得更加容易和高效。layer上不具备处理事件,绘制内容,响应链(responder chain)等
Layer也和View一样存在着一个层级树状结构,称之为图层树(Layer Tree),直接创建的或者通过UIView获得的(view.layer)用于显示的图层树,称之为模型树(Model Tree),模型树的背后还存在两份图层树的拷贝,一个是呈现树(Presentation Tree),一个是渲染树(Render Tree). 呈现树可以通过普通layer(其实就是模型树)的layer.presentationLayer获得,而模型树则可以通过modelLayer属性获得.模型树的属性在其被修改的时候就变成了新的值,这个是可以用代码直接操控的部分;呈现树的属性值和动画运行过程中界面上看到的是一致的.而渲染树是私有的,你无法访问到,渲染树是对呈现树的数据进行渲染,为了不阻塞主线程,渲染的过程是在单独的进程或线程中进行的,所以你会发现Animation的动画并不会阻塞主线程.
呈现树通过图层树中所有图层的呈现图层所形成。注意呈现图层仅仅当图层首次被提交(就是首次第一次在屏幕上显示)的时候创建,所以在那之前调用-presentationLayer将会返回nil。
layer.presentationLayer的运用,如果你要在一个运动的layer中判断,一个点是否在layer中,如果直接用layer可能出现,时间在现实中是在layer上,但是判断上并没有,因为这个时候layer的几何属性位置是和layer的位置有差别的,应该用layer.presentationLayer来判断。
具备的功能:
阴影,圆角,带颜色的边框
3D变换
非矩形范围
透明遮罩
多级非线性动画
二.CALayer使用
1.基本方法
1.1 初始化的3种方法
/*
+ (instancetype)layer;
- (instancetype)init;
- (instancetype)initWithLayer:(id)layer; 复制一个图层
*/
CALayer *layer = [CALayer layer];
// 设置layer的大小
layer.bounds = CGRectMake(0, 0, 100, 100);
// 设置位置居中
layer.position = self.view.center;
// 添加视图
[self.view.layer addSublayer:layer];
// 这里直接设置了一个图片到图层
UIImage *image2 = [UIImage imageNamed:@"2.jpg"];
layer.contents = (__bridge id _Nullable)(image2.CGImage);
效果图
1.2 层的添加,移除,插入,替换
sublayers:子layers
superlayer:父layer
- (void)addSublayer:(CALayer *)layer;
- (void)insertSublayer:(CALayer *)layer atIndex:(unsigned)idx;
- (void)insertSublayer:(CALayer *)layer below:(nullable CALayer *)sibling;
- (void)insertSublayer:(CALayer *)layer above:(nullable CALayer *)sibling;
- (void)replaceSublayer:(CALayer *)layer with:(CALayer *)layer2;
1.3相关层对象
呈现树通过图层树中所有图层的呈现图层所形成。注意呈现图层仅仅当图层首次被提交(就是首次第一次在屏幕上显示)的时候创建,所以在那之前调用-presentationLayer将会返回nil。
- (nullable instancetype)presentationLayer; 得到层的副本。得到的是当前层的副本,也就是说在动画的时候presentationLayer是不断改变的。当屏幕没有开始显示layer的时候,为nil
- (instancetype)modelLayer; 返回模型层。如果是-presentationLayer返回的呈现层调用则返回所依赖的CALayer;如果不是-presentationLayer返回的层调用则返回self。只有当涉及到表示层的更改时,该方法才会返回一个值。如果没有正在进行的事务,调用此方法的结果是未定义的。
1.4 渲染
当更新层,改变不能立即显示在屏幕上。当所有的层都准备好时,可以调用setNeedsDisplay方法来重绘显示。
[layer setNeedsDisplay];
若要重绘部分屏幕区域,请使用setNeedsDisplayInRect:方法,通过在CGRect结构的区域更新:
[layer setNeedsDisplayInRect:CGRectMake(0.0,0.0,50.0,75.0)];
如果是用的Core Graphics框架来执行渲染的话,可以直接渲染Core Graphics的内容。用renderInContext:来做这个事。
[layer renderInContext:UIGraphicsGetCurrentContext()];
当它的边界矩形变化时,层内容是否必须更新
layer.needsDisplayOnBoundsChange = NO;
正常更新周期之外强制更新您的层的内容,一般不使用。首选方法是调用setNeedsDisplay,让系统在下一次周期中更新层
[layer displayIfNeeded];
指示该层是否被标记为需要更新
[layer needsDisplay];
返回一个布尔值,表示对指定键的更改是否要求重新显示该层。
BOOL neetd_display = [CALayer needsDisplayForKey:@"position"];
NSLog(@"%d", neetd_display);
1.5 动画的添加,移除
- (void)addAnimation:(CAAnimation *)anim forKey:(nullable NSString *)key;
- (void)removeAllAnimations;
- (void)removeAnimationForKey:(NSString *)key;
- (nullable NSArray<NSString *> *)animationKeys;
- (nullable CAAnimation *)animationForKey:(NSString *)key;
1.6 层调整和布局
指示层的sublayers的布局已经更改并且必须更新,当该layer的边界发生变化,系统通常会自动调用该方法。
- (void)setNeedsLayout;
子类可以覆盖此方法来给sublayer布局,详情可以看9。
- (void)layoutSublayers;
遍历层的超级层,直到找到不需要布局的父层,然后在祖先的整个层上执行布局。
- (void)layoutIfNeeded;
返回一个布尔值,指示该层是否被标记为需要一个布局更新。
- (BOOL)needsLayout;
最适合的尺寸
- (CGSize)preferredFrameSize;
1.7 Scrolling
visibleRect - 返回layer在屏幕显示的内容的位置
滚动到某一点,只有CAScrollLayer有效
- (void)scrollPoint:(CGPoint)p;
滚动到某一个区域可见,只有CAScrollLayer有效
- (void)scrollRectToVisible:(CGRect)r;
1.8 时间转换方法:用来得到layer之间通过speed,timeOffset等参数因数在内的时间转换后的时间
- (CFTimeInterval)convertTime:(CFTimeInterval)t fromLayer:(CALayer *)l;
- (CFTimeInterval)convertTime:(CFTimeInterval)t toLayer:(CALayer *)l;
2.contents属性
contents给CALayer赋予内容,类型为id类型,意味着它可以是任何类型的对象。但是在ios中你只能把CGImageRef赋值给它,如果不是CGImage得到的内容会是空。赋值方法和效果可以参考上面1的例子。
这里主要讲如何给layer赋内容,主要有3种方法:
a.图像对象直接赋给层对象的内容属性(这种技术最适合用于不需要或很少更改的层内容。)
b.使用CALayerDelegate委托绘制层的内容。(此技术最适合于可能周期性变化的层内容,可以由外部对象提供,例如view)
c.定义一个layer子类,并覆盖它的绘图方法之一,以自己提供layer内容。(如果你需要创建一个自定义layer子类,或者你想改变层的基本绘图行为,这个技巧是合适的。)
2.1第一种方法
UIImage *image = [UIImage imageNamed:@"123.jpg"];
self.view.layer.contents = (__bridge id _Nullable)(image.CGImage);
2.2第二种方法
// 说明:对于UIView应该使用drawRect:来绘图,不要使用这个代理。UIView自动使它自己成为layer的委托,并实现所需的委托方法
CALayer *layer = [CALayer layer];
layer.bounds = CGRectMake(0, 0, 100, 100);
layer.position = self.view.center;
[self.view.layer addSublayer:layer];
layer.delegate = self;
// 告诉layer需要更新显示,不适用这个不会调用委托的方法
[layer setNeedsDisplay];
#pragma mark - contents属性方法2 <CALayerDelegate>
// CALayerDelegate必须实现displayLayer:或drawLayer:inContext:方法。如果CALayerDelegate实现了displayLayer和drawLayer:inContext:方法,层只调用displayLayer:方法。
// 该实现负责创建位图并将其分配到该层的内容属性
- (void)displayLayer:(CALayer *)layer {
UIImage *image = [UIImage imageNamed:@"2.jpg"];
layer.contents = (__bridge id _Nullable)(image.CGImage);
}
// 创建一个图形上下文来绘制位图,然后调用你的委托方法来填充位图
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {
CGMutablePathRef thePath = CGPathCreateMutable();
CGPathMoveToPoint(thePath,NULL,15.0f,15.f);
CGPathAddCurveToPoint(thePath,
NULL,
15.f,250.0f,
295.0f,250.0f,
295.0f,15.0f);
CGContextBeginPath(ctx);
CGContextAddPath(ctx, thePath);
CGContextSetLineWidth(ctx, 5);
CGContextStrokePath(ctx);
// Release the path
CFRelease(thePath);
}
2.3第三种方法
#import "CACustomLayer.h"
@implementation CACustomLayer
// 第一种
// 第一种实现了不会调用第二种
- (void)display {
UIImage *image = [UIImage imageNamed:@"2.jpg"];
self.contents = (__bridge id _Nullable)(image.CGImage);
}
// 第二种
- (void)drawInContext:(CGContextRef)ctx {
CGMutablePathRef thePath = CGPathCreateMutable();
CGPathMoveToPoint(thePath,NULL,15.0f,15.f);
CGPathAddCurveToPoint(thePath,
NULL,
15.f,250.0f,
295.0f,250.0f,
295.0f,15.0f);
CGContextBeginPath(ctx);
CGContextAddPath(ctx, thePath);
CGContextSetLineWidth(ctx, 5);
CGContextStrokePath(ctx);
// Release the path
CFRelease(thePath);
}
@end
CACustomLayer *layer = [[CACustomLayer alloc] init];
layer.bounds = CGRectMake(0, 0, 100, 100);
layer.position = CGPointMake(CGRectGetMidX(self.view.frame), CGRectGetMidY(self.view.frame));
[self.view.layer addSublayer:layer];
// 告诉layer需要更新显示,不使用这个不会layer重写的方法
[layer setNeedsDisplay];
3.CALayer的几何学
这里我使用同一个layer的介绍下面的内容
// 创建一个layer
UIImage *background_image = [UIImage imageNamed:@"123.jpg"];
self.view.layer.contents = (__bridge id)background_image.CGImage;
CALayer *layer = [CALayer layer];
UIImage *image = [UIImage imageNamed:@"2.jpg"];
layer.contents = (__bridge id)image.CGImage;
[self.view.layer addSublayer:layer];
3.1 CALayer布局
// frame 和view中的frame类似,表示在父layer里面的位置
// bounds 和view中的bounds类似,表示自己的大小
// position 和view中的center类似,表示在父layer里的中心位置
// 注:这些赋值参数尽量使用整数
layer.bounds = CGRectMake(0, 0, 100, 100);
layer.position = CGPointMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds));
// 这里设置frame和设置bounds,position一致
// layer.frame = CGRectMake(CGRectGetMidX(self.view.bounds) - 50, CGRectGetMidY(self.view.bounds) - 50, 100, 100);
3.2 锚点anchorPoint
这种瞄点可以改变视图的位置,默认值是[0.5,0.5],如果设置为[0.0,0.0],视图就会往右下移,显示的开始位置变成了原先显示的中点位置。简单的说,position,bounds,anchorPoint决定了视图的frame。
锚点在ios中和mac os 一点不同,了解具体的内容可以自行查阅资料,也可以看官方文档。
例子:
// 设置layer的位置在父layer中心让左上分别偏移50的位置
layer.position = CGPointMake(CGRectGetMidX(self.view.bounds) - 50, CGRectGetMidY(self.view.bounds) - 50);
// 这里设置layer又显示到了中心位置。
layer.anchorPoint = CGPointMake(0.0, 0.0);
3.3 不同坐标系之间的图层转换方法
- (CGPoint)convertPoint:(CGPoint)p fromLayer:(nullable CALayer *)l;
- (CGPoint)convertPoint:(CGPoint)p toLayer:(nullable CALayer *)l;
- (CGRect)convertRect:(CGRect)r fromLayer:(nullable CALayer *)l;
- (CGRect)convertRect:(CGRect)r toLayer:(nullable CALayer *)l;
调用的layer和l必须共享同一个父layer。
l可以为nil,如果您为l参数指定nil,这个方法将返回从该层的原点减去的原始点。
例子:
CALayer *layer2 = [CALayer layer];
layer2.backgroundColor = [UIColor yellowColor].CGColor;
layer2.bounds = CGRectMake(0, 0, 100, 100);
layer2.position = CGPointMake(CGRectGetMidX(self.view.bounds) + 50, CGRectGetMidY(self.view.bounds) + 50);
[self.view.layer insertSublayer:layer2 below:layer];
// 得到layer的CGPointMake(0, 0)在屏幕显示点,在_displayLayer.layer2为坐标参考对应的坐标
CGPoint point1 = [layer convertPoint:CGPointMake(0, 0) fromLayer:layer2];
NSLog(@"%@", NSStringFromCGPoint(point1));
CGPoint point2 = [layer2 convertPoint:CGPointMake(0, 0) toLayer:layer];
NSLog(@"%@", NSStringFromCGPoint(point2));
// 得到self.view.layer的CGRectMake(0, 0, 1, 1)在屏幕显示点,在layer为坐标参考对应的坐标
NSLog(@"%@", NSStringFromCGRect(layer.frame));
CGRect rect1 = [layer convertRect:CGRectMake(0, 0, 1, 1) fromLayer:self.view.layer];
NSLog(@"%@", NSStringFromCGRect(rect1));
CGRect rect2 = [self.view.layer convertRect:CGRectMake(0, 0, 1, 1) toLayer:layer];
NSLog(@"%@", NSStringFromCGRect(rect2));
3.4 hitTest
返回p点,在层层次结构最底层的layer
- (CALayer *)hitTest:(CGPoint)p;
返回是否包含指定的点。
- (BOOL)containsPoint:(CGPoint)p;
3.5 内容垂直翻转。不影响内容,只影响layer的几何属性。
layer.geometryFlipped = NO;
4.显示效果调整属性
新建一个layer来说明:
// 创建一个layer
UIImage *background_image = [UIImage imageNamed:@"123.jpg"];
self.view.layer.contents = (__bridge id)background_image.CGImage;
CALayer *layer = [CALayer layer];
UIImage *image = [UIImage imageNamed:@"2.jpg"];
layer.contents = (__bridge id)image.CGImage;
layer.bounds = CGRectMake(0, 0, 100, 100);
layer.position = CGPointMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds));
[self.view.layer addSublayer:layer];
4.1 圆角和边界裁剪 cornerRadius masksToBounds
// 设置圆角为宽度的一半
// 边界裁剪默认为no,所有设置圆角需要边界裁剪设置为yes
layer.cornerRadius = 50;
layer.masksToBounds = YES;
4.2 背景颜色
layer.backgroundColor = [UIColor whiteColor].CGColor;
4.3 边框, 边框的设置在设置圆角的时候,会受圆角影响
// 边框颜色
layer.borderColor = [UIColor orangeColor].CGColor;
// 边框宽度
layer.borderWidth = 1;
效果图:
4.4 阴影效果
shadowOpacity 控制阴影的透明度,默认为0,完全透明
shadowColor 控制阴影的颜色,默认是黑色
shadowOffset 控制阴影的方向和距离,它是一个CGSize的值,宽度控制这阴影横向的位移,高度控制着纵向的位移,默认值是 {0, -3},意即阴影相对于Y轴有3个点的向上位移(主要是mac os 里面的坐标系统,所以默认是向上的,ios中,自己设置就可以了)。
shadowPath 阴影效果显示,可以由UIBezierPath来控制路径,比如设置一个圆的阴影效果
由于阴影效果是在layer外显示的,所以masksToBounds为yes的时候,会被裁剪,不会显示。解决这种方法通常在外面套一个layer实现阴影效果
// 创建一个显示阴影的layer
CALayer *shadowLayer = [CALayer layer];
shadowLayer.bounds = CGRectMake(0, 0, 100, 100);
shadowLayer.position = CGPointMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds));
[self.view.layer addSublayer:layer];
// 设置阴影效果
shadowLayer.shadowOpacity = 0.6;
shadowLayer.shadowColor = [UIColor orangeColor].CGColor;
shadowLayer.shadowOffset = CGSizeMake(0, 0);
// 添加shadowLayer在layer层的下面
[self.view.layer insertSublayer:shadowLayer below:layer];
// 因为layer被裁剪为圆形,所有阴影效果要保持一直,通过shadowPath来设置。
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 100, 100) cornerRadius:50];
shadowLayer.shadowPath = bezierPath.CGPath;
效果图:
4.5 图层蒙版
mask属性就像是一个饼干切割机,mask图层实心的部分会被保留下来,其他的则会被抛弃
UIImage *maskImage = [UIImage imageNamed:@"t"];
CALayer *maskLayer = [CALayer layer];
maskLayer.frame = layer.bounds;
maskLayer.contents = (__bridge id _Nullable)(maskImage.CGImage);
layer.mask = maskLayer;
// 为了显示效果暂时把shadowLayer隐藏
shadowLayer.hidden = YES;
// // 去除蒙版,回复原先的显示效果
// layer.mask = nil;
// shadowLayer.hidden = NO;
效果图:
4.6 组透明度
类似于UIView的alpha,layer也有一个opacity属性来设置layer的透明度。
在layer设置opacity的时候,会影响子layer的透明,这个主要是由allowsGroupOpacity组不透明度来控制,这个值取决于Info.plist设置,在ios7之后默认为yes,就是说layer设置了opacity会整合子layer的透明度,看起来他们就是一个试图,透明度一样。当然如果设置为no的时候,会产生子试图的透明度和layer不一致。
调整layer:
// 在layer上,添加一个黑色的subLayer
CALayer *subLayer = [CALayer layer];
subLayer.backgroundColor = [UIColor blackColor].CGColor;
subLayer.frame = CGRectMake(20, 20, 60, 60);
[layer addSublayer:subLayer];
// 为了体现出区别,我这里设置视图往下移
layer.position = CGPointMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds) + 200);
// 隐藏shadowLayer
shadowLayer.hidden = YES;
总结:
1.设置layer的opacity会对subLayer产生影响
2.设置subLayer的opacity会在layer参数产生影响的基础上再产生影响。
3.allowsGroupOpacity决定了subLayer能对layer的opacity参生不同的效果,layer的透视效果㛑发送了改变。
4.为了不要对比得复杂,我这里没有列举出全部的例子,权权是启到一个知识回顾提示作用。
还可以设置CALayer的一个叫做shouldRasterize属性来实现组透明的效果,如果它被设置为YES,在应用透明度之前,图层及其子图层都会被整合成一个整体的图片,这样就没有透明度混合的问题了
如果你使用了shouldRasterize,那么需要通过rasterizationScale属性去匹配屏幕,以防止出现Retina屏幕像素化的问题
注:当shouldRasterize和UIViewGroupOpacity一起的时候,会出现性能问题,这个在图层性能上会提到。
// layer.shouldRasterize = YES;
// layer.rasterizationScale = 2;
4.7 显示和隐藏
layer.hidden = NO;
4.8 可以通过style属性给属性赋值,如果已经给layer属性赋过值,那么给这个属性设置将不起作用。这个属性一般在对进行自定义属性,添加隐式动画的时候使用
NSMutableDictionary *dic = [NSMutableDictionary dictionary];
[dic setObject:(__bridge id)[UIColor redColor].CGColor forKey:@"borderColor"];
[dic setObject:@2 forKey:@"borderWidth"];
layer.style = dic;
4.9 allowsEdgeAntialiasing 允许边缘抗锯齿
4.10 edgeAntialiasingMask 使用此属性禁用对其他层边缘的反锯齿,以消除可能发生的边界
4.11 和contents内容设置有关的参数
// contentsGravity 内容布局方式
layer.contentsGravity = kCAGravityResizeAspectFill;
// contentsScale 内容倍数
layer.contentsScale = [UIScreen mainScreen].scale;
// contentsRect 属性允许我们在图层边框里显示寄宿图的一个子域, 比如:把图片按中心分成4个部分,只显示图片的后下半部分[0.5, 0.5, 1, 1],前上部分[0.0, 0.0, 0.5, 0.5];
layer.contentsRect = CGRectMake(0.0, 0.0, 1, 1);
// contentsCenter内容中心,可以配合contentsGravity进行内容显示调整,他工作起来的效果和UIImage里的-resizableImageWithCapInsets: 方法效果非常类似。简单的说,设置这个会改变图片的拉伸效果,默认情况下,contentsCenter是{0, 0, 1, 1},这意味着如果大小(由conttensGravity决定)改变了,那么寄宿图将会均匀地拉伸开。但是如果我们增加原点的值并减小尺寸{0.25, 0.25, 0.5, 0.5},就只会在这个部分就行拉伸。
layer.contentsCenter = CGRectMake(0.0, 0.0, 1.0, 1.0);
// contentsAreFlipped 显示的内容是否翻转
// opaque 内容是否不透明,只影响由核心动画管理的备份存储。如果内容不包含alpha通道,设置为yes,可以提高性能。
layer.opaque = NO;
// 是否在后台绘制内容
layer.drawsAsynchronously = NO;
// contentsFormat 内容显示的格式,layer 和view 会自动调整适合的格式,一般不需要去设置
// layer.contentsFormat = kCAContentsFormatRGBA8Uint;
5.变换
对layer显示就行调整,只是改变了显示效果,没有对他的几何属性做改变。主要有:旋转,缩放和平移.
a.仿射变换CGAffineTransform
b.3D变换CATransform3D
6.Key-Value Coding Extensions
核心动画扩展了NSKeyValueCoding协议,因为它属于CAAnimation和CALayer类。此扩展为一些键添加默认值,展开包装约定,并为CGPoint、CGRect、CGSize和CATransform3D类型添加关键路径支持。官方文档
关于kvc可以自行去了解,这里主要对layer的+ (id)defaultValueForKey:(NSString *)key做一个说明。kvc官方文档
// 返回是否归档
- (BOOL)shouldArchiveValueForKey:(NSString *)key;
设置默认值,重写用
+ (id)defaultValueForKey:(NSString *)key;
#import "CAKeyValueCodingLayer.h"
@implementation CAKeyValueCodingLayer
// NSKeyValueCoding 设置默认返回key对应的值
// 对用设置的值类型
+ (id)defaultValueForKey:(NSString *)key {
if ([key isEqualToString:@"masksToBounds"]) {
return [NSNumber numberWithBool:YES];
}
return [super defaultValueForKey:key];
}
@end
CAKeyValueCodingLayer *layer = [CAKeyValueCodingLayer layer];
NSLog(@"masksToBounds = %@", [layer valueForKey:@"masksToBounds"]);
NSLog(@"someKey1 = %@", [layer valueForKey:@"someKey"]);
// 支持 NSKeyValueCoding Key-Value Coding Extensions
[layer setValue:[NSNumber numberWithInteger:50] forKey:@"someKey"];
NSNumber *someKeyValue = [layer valueForKey:@"someKey"];
NSLog(@"someKey2 = %@", someKeyValue);
masksToBounds默认的值应该为no,上面的代码把默认值设为了yes。
7.改变view默认带有layer的类型
view默认带有layer,可以通过重写+ (Class)layerClass改变layer的类型
大多数view默认会备份这个view。一般情况下使用默认就可以。
但是可能需要更改的情况,列举几种情况(创建后,视图的图层对象不能更改。):
a.使用 Metal 和 OpenGL ES,来进行绘制,使用 CAMetalLayer or CAEAGLLayer
b.使用一个专门的layer类型,来提高性能
c.利用一些专门的核心动画层类,例如粒子发射器或复制器,CAEmitterLayer,CAReplicatorLayer
#import "CAMetalLayerView.h"
@implementation CAMetalLayerView
+ (Class)layerClass {
return [CAMetalLayer class];
}
@end
CAMetalLayerView *view = [[CAMetalLayerView alloc] init];
NSLog(@"%@", NSStringFromClass(view.layer.class));
这里把UIView的layer类型改成了 CAMetalLayer。
8.手动给sublayers布局
手动布局主要有2种方式:
a.实现CALayerDelegate代理- (void)layoutSublayersOfLayer:(CALayer *)layer
b.自定义layer实现覆盖父类- (void)layoutSublayers;
#import <QuartzCore/QuartzCore.h>
@interface CACustomDisplayLayer : CALayer
@property (nonatomic, strong) CALayer *layer1;
@property (nonatomic, strong) CALayer *layer2;
@property (nonatomic, strong) CALayer *layer3;
@end
#import "CACustomDisplayLayer.h"
#import <UIKit/UIKit.h>
@implementation CACustomDisplayLayer
// 自定义Layer, 覆盖这个实现,可以实现手动给sublayer布局。
- (void)layoutSublayers {
[super layoutSublayers];
CGFloat width = floor((CGRectGetWidth(self.bounds) - 24) * 0.5);
CGFloat height = floor((CGRectGetHeight(self.bounds) - 24) * 0.5);
self.layer1.frame = CGRectMake(8, 8, width, height);
self.layer2.frame = CGRectMake(16 + width, 8, width, height);
self.layer3.frame = CGRectMake(8, 16 + height, width, height);
}
- (CALayer *)layer1 {
if (!_layer1) {
_layer1 = [CALayer layer];
_layer1.backgroundColor = [UIColor yellowColor].CGColor;
[self addSublayer:_layer1];
}
return _layer1;
}
- (CALayer *)layer2 {
if (!_layer2) {
_layer2 = [CALayer layer];
_layer2.backgroundColor = [UIColor redColor].CGColor;
[self addSublayer:_layer2];
}
return _layer2;
}
- (CALayer *)layer3 {
if (!_layer3) {
_layer3 = [CALayer layer];
_layer3.backgroundColor = [UIColor blueColor].CGColor;
[self addSublayer:_layer3];
}
return _layer3;
}
@end
// CACustomDisplayLayer 里面只有3一样,但是颜色不一样的图形
CACustomDisplayLayer *displayLayer = [CACustomDisplayLayer layer];
displayLayer.bounds = CGRectMake(0, 0, 200, 200);
displayLayer.position = CGPointMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds));
displayLayer.backgroundColor = [UIColor orangeColor].CGColor;
displayLayer.opacity = 0.6;
displayLayer.delegate = self;
[self.view.layer addSublayer:displayLayer];
// 再最后再添加一个颜色不一样,其他一样的图形
CALayer *sub_displayLayer = [CALayer layer];
sub_displayLayer.backgroundColor = [UIColor grayColor].CGColor;
[displayLayer addSublayer:sub_displayLayer];
// 4秒之后改变displayLayer.bounds,观察 displayLayer,sublayers是否自定调节
// dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//
// displayLayer.bounds = CGRectMake(0, 0, 150, 150);
// });
这样做有一个好处,就是当你改变了layer的大小,layer会自动调用相应的方法,对子layer做调整。
三.CALayer其他使用
待补充