专用图层(上)
CALayer的孩子们
专用图层是CALayer
的子类,各自具有不同的功能,使用之后能够进一步扩展使用Core Animation绘图的能力。
专用图层的绘制通常是使用硬件加速完成的,通常会具有较好的性能。且相较于使用UIView实现相应效果,CALayer
会自带隐式动画,能有更为流畅的变化效果。
目录
CAShapeLayer - 矢量图层
属性
通过CGPath绘制图形
CAGradientLayer - 渐变图层
属性
基础渐变
多重渐变
基于渐变图层的一些的特效
小结
CAShapeLayer - 矢量图层
在上一节中,我们已经初步使用过CAShapeLayer
。CAShapeLayer
是一个通过矢量图形而不是bitmap来绘制的图层子类,可以指定颜色
线宽
等属性,用path
属性定义需要绘制的图形。相比较于通过使用Core Graphics
进行绘制,他具有以下优点:
- 渲染快速。
CAShapeLayer
使用了硬件加速,绘制同一图形会比用Core Graphics快很多。 - 高效使用内存。一个
CAShapeLayer
不需要像普通CALayer
一样创建一个寄宿图形,所以无论有多大,都不会占用太多的内存。 - 不会被图层边界剪裁掉。一个
CAShapeLayer
可以在边界之外绘制。 - 不会出现像素化。当你给
CAShapeLayer
做3D变换时,它不像一个有寄宿图的普通图层一样变得像素化。
path
属性是一个CGPathRef
类型对象,可以由(UIBezierPath *)path.CGPath
转换获得
属性
-
@property(nullable) CGPathRef path;
:决定layer的形状 -
@property(nullable) CGColorRef fillColor;
:决定layer的填充颜色,设置为[UIColor ClearColor]
时不进行填充 -
@property(nullable) CGColorRef strokeColor;
:决定layer的线条颜色
以上是最常用的属性
-
@property CGFloat strokeEnd;
:线条的结束位置(0~1,对应于path而言,默认为1) -
@property CGFloat strokeStart;
:线条的起始位置(0~1,对应于path而言,默认为0)
strokeEnd
与strokeStart
属性通常用于做动画,比如你可以试一下[NSTimer scheduledTimerWithTimeInterval:0.01 repeats:YES block:^(NSTimer * _Nonnull timer) { shapeLayer.strokeStart += 0.005; }];
你会看到你的Path慢慢的就变短了。
[写到此处周老师忽然意识到进度条可以用这个东西来写]
-
@property(copy) NSString *lineCap;
:path端点样式字符串常量,有3种样式- ``kCALineCapButt`, // 无端点
- ``kCALineCapRound`, // 圆形端点
- ``kCALineCapSquare` // 方形端点
下图从上到下依次为
kCALineCapButt
kCALineCapRound
kCALineCapSquare
样式。
kCALineCapButt
样式上和kCGLineCapButt
是一样的,但是长度会长一丢丢
-
@property(copy) NSString *lineJoin;
:拐角样式字符串常量,有3种样式-
kCALineJoinMiter
, // 尖角 -
kCALineJoinRound
, // 圆角 -
kCALineJoinBevel
// 缺角
-
-
miterLimit
:最大斜接长度(只有在使用kCALineJoinMiter是才有效), 边角的角度越小,斜接长度就会越大,为了避免斜接长度过长,使用lineLimit属性限制,如果斜接长度超过miterLimit,边角就会以kCALineJoinBevel
类型来显示
下图从上到下依次为
kCALineJoinMiter
kCALineJoinRound
kCALineJoinBevel
样式。此时miterLimit
的值足够大,故会有一个很长很长的斜角。
-
@property(nullable, copy) NSArray
:虚线数组(单位为pt)*lineDashPattern; -
@property CGFloat lineDashPhase;
:虚线的偏移量(单位为pt)(可动画属性,修改后会触发隐式动画)
//设置虚线数组为[2,5,10],数组会按数组元素循环截断线条(2pt为红,5pt为透明,10pt为红,2pt为白.....) shapeLayer.lineDashPattern = @[@2, @5, @10]; //lineDashPhase的动画属性,能够事实更改并生效 [NSTimer scheduledTimerWithTimeInterval:0.02 repeats:YES block:^(NSTimer * _Nonnull timer) { shapeLayer.lineDashPhase += 1; }];
通过CGPath绘制图形
CAShapeLayer
能够绘制所有可以通过CGPath
来表示的形状,不论闭合与否,且可以在一个图层上绘制多个不同的形状。其次可以控制一些属性比如lineWith
(线宽,用点表示单位),lineCap
(线条结尾的样子),和lineJoin
(线条之间的结合点的样子);但是在一个图层中,所有这些属性只能有一个样式,所以你如果想用不同的颜色/粗细/风格来绘制多个形状,那就需要使用多个图层。
接下来是用CAShapeLayer
绘制一个火柴人的例子:
UIBezierPath *path = [[UIBezierPath alloc] init];
[path moveToPoint:CGPointMake(175, 100)];
//下面是画图的代码
[path addArcWithCenter:CGPointMake(150, 100) radius:25 startAngle:0 endAngle:2*M_PI clockwise:YES];
[path moveToPoint:CGPointMake(150, 125)];
[path addLineToPoint:CGPointMake(150, 175)];
[path addLineToPoint:CGPointMake(125, 225)];
[path moveToPoint:CGPointMake(150, 175)];
[path addLineToPoint:CGPointMake(175, 225)];
[path moveToPoint:CGPointMake(100, 150)];
[path addLineToPoint:CGPointMake(200, 150)];
//创建shapeLayer,配置属性
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.strokeColor = [UIColor redColor].CGColor;//线条颜色为红色
shapeLayer.fillColor = [UIColor clearColor].CGColor;//填充颜色为透明,否则会对线条包围的部分进行填充
shapeLayer.lineWidth = 5;
shapeLayer.lineJoin = kCALineJoinRound;//圆形折角
shapeLayer.lineCap = kCALineCapRound;//圆形断点
shapeLayer.path = path.CGPath;
//添加至superLayer后生效
[self.view.layer addSublayer:shapeLayer];
CAGradientLayer - 渐变图层
CAGradientLayer
是用来生成两种或更多颜色平滑渐变的,绘制使用了硬件加速,同时也可以用Core Graphics
复制一个CAGradientLayer
并将内容绘制到一个普通图层的寄宿图。
属性
-
@property(nullable, copy) NSArray *colors
:渐变的颜色数组,数组成员接受CGColorRef
,非NSObject对象,所以需要bridge
转换。默认选择均匀分布 -
@property(nullable, copy) NSArray
:渐变颜色数组对应的点坐标,延渐变轴线分布的0~1之间的浮点数。可选属性,若赋值则必须与*locations colors
数量一致 -
@property CGPoint startPoint
:渐变的起始点,点坐标 -
@property CGPoint endPoint
:渐变的终点,点坐标,渐变以起点与终点连线作为渐变轴进行渐变
例:设置
startPoint
为{0, 0.5},endPoint
为{1, 0.5},渐变轴的方向
-
@property(copy) NSString *type
:渐变绘制样式,目前只有一种-
kCAGradientLayerAxial
:轴向渐变
-
基础渐变
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
gradientLayer.frame = CGRectMake(100, 100, 200, 200);
[self.view.layer addSublayer:gradientLayer];
//设置渐变颜色为[红, 蓝]
gradientLayer.colors = @[(__bridge id)[UIColor redColor].CGColor, (__bridge id)[UIColor blueColor].CGColor];
//设置起点为左上角,重点为右下角
gradientLayer.startPoint = CGPointMake(0, 0);
gradientLayer.endPoint = CGPointMake(1, 1);
多重渐变
colors
属性可以包含很多颜色,locations
属性定义了这些颜色的位置(沿轴线分布的距离)。
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
gradientLayer.frame = CGRectMake(100, 100, 200, 200);
[self.view.layer addSublayer:gradientLayer];
gradientLayer.colors = @[(__bridge id)[UIColor redColor].CGColor,
(__bridge id) [UIColor yellowColor].CGColor,
(__bridge id)[UIColor greenColor].CGColor];
gradientLayer.locations = @[@0.0, @0.25, @0.5];
gradientLayer.startPoint = CGPointMake(0, 0);
gradientLayer.endPoint = CGPointMake(1, 1);
一些骚骚的特效
虽然渐变图层看起来土土的还很乡非,但是配合其他绘制机制,可以组合变化出一些很酷炫的操作。
比如配合蒙板可以实现:
这个效果的原理是:渐变图层做渐变动画,使用一个UILabel.layer
作为蒙板,达到文字发生渐变的效果。
- 首先我们需要创建一个黑白相间的渐变图层,并为其添加动画效果
- 然后再创建一个UILabel,取它的layer作为gradientLayer的蒙板
//创建渐变图层
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
[self.view.layer addSublayer:gradientLayer];
gradientLayer.frame = CGRectMake(0, 200, kScreenWidth, 64);
//设置渐变轴为从左往右
gradientLayer.startPoint = CGPointMake(0, 0.5);
gradientLayer.endPoint = CGPointMake(1, 0.5);
//黑白相间的颜色
gradientLayer.colors = @[
(__bridge id)[UIColor blackColor].CGColor,
(__bridge id)[UIColor whiteColor].CGColor,
(__bridge id)[UIColor blackColor].CGColor,];
//因为要做动画,所以locations属性可以是任何NSArray对象就可以了
//甚至你可以这样写: .locations = @[@"干死黄旭东", @"蟑螂加两攻"];
//下面是规范写法
gradientLayer.locations = @[@0.25,@0.5,@0.75];
//添加动画
//动画KeyPath指定为"locations",表示要对gradientLayer.locations属性做动画
CABasicAnimation *basicAnimation = [CABasicAnimation animationWithKeyPath:@"locations"];
basicAnimation.fromValue = @[@0, @0, @0.25];
basicAnimation.toValue = @[@0.75, @1, @1];
basicAnimation.duration = 2.5;
basicAnimation.repeatCount = HUGE;
[gradientLayer addAnimation:basicAnimation forKey:nil];
//接下来创建作为蒙板的Label
//注意这个frame,是相对gradientLayer的坐标
UILabel *label = [[UILabel alloc] initWithFrame:gradientLayer.bounds];
label.text = @"滑动狗头解锁 >>";
label.alpha = 0.5;
label.textAlignment = NSTextAlignmentCenter;
label.font = [UIFont systemFontOfSize:30];
//一定要强引用这个Label避免被释放
self.label = label;
//重点:设置Label的layer为gradientLayer的蒙板,不需要做addSubView操作
gradientLayer.mask = label.layer;
小结
CAShapeLayer
与CAGradientLayer
都是具有绘制作用的专用图层,而且他们的绘制都是使用硬件加速完成的,他们通常能够以相对较高的性能完成一些简单的绘制和显示效果工作。例如在日常工作中遇到需要做简单填充效果的时候,可以使用专用图层来替代Core Graphics
提升性能,而且由于CALayer带有隐式动画,还可以省去动画调度代码的编写。