支持原创,原文地址:www.KentonYu.com
CAShapeLayer
CAShapeLayer 是一个通过矢量图形而不是 bitmap 来绘制的图层子类。可以指定颜色、线宽等属性,用CGPath 来定义想要绘制的图形,最后 CAShapeLayer 就会自动渲染出来了。当然,你也可以用 Core Graphics 直接向原始的 CALyer 的内容中绘制一个路径(<code>- drawLayer: inContext:</code>),相比之下,使用 CAShapeLayer 有以下一些优点:
- 渲染快速。CAShapeLayer 使用了硬件加速,绘制同一图形会比用 Core Graphics 快很多。
- 高效使用内存。一个 CAShapeLayer 不需要像普通 CALayer 一样创建一个寄宿图形,所以无论有多大,都不会占用太多的内存。
- 不会被图层边界剪裁掉。一个 CAShapeLayer 可以在边界之外绘制。你的图层路径不会像在使用 Core Graphics 的普通 CALayer 一样被剪裁掉.
- 不会出现像素化。当你给 CAShapeLayer 做 3D 变换时,它不像一个有寄宿图的普通图层一样变得像素化。
主要属性
<pre>
// CAShapeLayer 绘制的路径
@property(nullable) CGPathRef path;
//路径中的填充颜色
@property(nullable) CGColorRef fillColor;
//填充规则
@property(copy) NSString *fillRule;
//画笔颜色(路径的颜色,边框颜色)
@property(nullable) CGColorRef strokeColor;
//这是一组范围值,路径绘制开始和结束的范围(0 -> 1)
@property CGFloat strokeStart;
@property CGFloat strokeEnd;
//设置虚线显示的起点距离,设置为8,则显示长度8之后的线
@property CGFloat lineDashPhase;
//设置虚线线段的长度和空格的长度,@[@20,@30,@40,@50],画20空30画40空50
@property(nullable, copy) NSArray<NSNumber *> *lineDashPattern;
//以下属性参见 UIBezierPath 的介绍
@property CGFloat lineWidth;
@property CGFloat miterLimit;
@property(copy) NSString *lineCap;
@property(copy) NSString *lineJoin;
</pre>
Show You Code
先上本 Demo 截图。
1.使用 CAShapeLayer 绘制一个圆角矩形:
<pre>
pragma mark Getter
(UIBezierPath *)path {
if (!_path) {
_path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 100.f, CGRectGetWidth([UIScreen mainScreen].bounds), 44.f) cornerRadius:22.f];
}
return _path;
}(CAShapeLayer *)shapeLayer {
if (!_shapeLayer) {
_shapeLayer = ({
CAShapeLayer *layer = [[CAShapeLayer alloc] init];
layer.path = self.path.CGPath;
layer.lineWidth = 2.f;
layer.strokeColor = [UIColor greenColor].CGColor;
layer.fillColor = [UIColor redColor].CGColor;
// strokeStart 绘制起点 strokeEnd 绘制终点 取值是 0-1
layer.strokeStart = 0;
layer.strokeEnd = 0.7f;
layer;
});
}
return _shapeLayer;
}
</pre>
2.绘制一根不同间隔,不同长度的虚线
<pre>
pragma mark Getter
(UIBezierPath *)dashLinePath {
if (!_dashLinePath) {
_dashLinePath = ({
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(20.f, 180.f)];
[path addLineToPoint:CGPointMake(CGRectGetWidth(self.frame) - 20.f, 180.f)];
path;
});
}
return _dashLinePath;
}(CAShapeLayer *)dashLineShapeLayer {
if (!_dashLineShapeLayer) {
_dashLineShapeLayer = ({
CAShapeLayer *layer = [[CAShapeLayer alloc] init];
layer.path = self.dashLinePath.CGPath;
layer.lineDashPhase = 8;
layer.lineDashPattern = @[@10, @20, @30, @60];
layer.strokeColor = [UIColor greenColor].CGColor;
layer.lineWidth = 2.f;
layer;
});
}
return _dashLineShapeLayer;
}
</pre>
lineDashPhase:绘制的虚线显示在屏幕上的起点,比如设置为10,则从整条线的 10 的位置开始才显示。
lineDashPattern:绘制虚线的格式,@[@10, @20, @30, @60],画10个点的线空20个点,以此类推。如果只设置一个元素,则线和间隔宽度相等。
3.使用 fillRule 属性,实现两个区域的取非
<pre>
pragma mark Getter
(UIBezierPath )fillRulePath {
if (!_fillRulePath) {
//这里先绘制哪个 Path 效果一样
_fillRulePath = [UIBezierPath bezierPathWithRect:CGRectMake(20.f, 200.f, CGRectGetWidth(self.frame)-40.f, 200.f)];
[_fillRulePath appendPath:[UIBezierPath bezierPathWithArcCenter:CGPointMake(CGRectGetWidth(self.frame)/2.f, 300.f) radius:50.f startAngle:0 endAngle:2M_PI clockwise:NO]];
}
return _fillRulePath;
}(CAShapeLayer *)fillRuleShapeLayer {
if (!_fillRuleShapeLayer) {
_fillRuleShapeLayer = ({
CAShapeLayer *layer = [[CAShapeLayer alloc] init];
layer.path = self.fillRulePath.CGPath;
layer.fillRule = kCAFillRuleEvenOdd;
layer.fillColor = [UIColor yellowColor].CGColor;
layer;
});
}
return _fillRuleShapeLayer;
}
</pre>
填充规则介绍:
kCAFillRuleNonZero:默认值,非零规则,当这个点作任意方法的射线,然后看射线和路径的交点方向,选择一个作为基准方向,如果方向一致则加1,方向不一致则减1。为0时,点不在路径内。
kCAFillRuleEvenOdd:奇偶规则,当这个点作任意方法的射线,射线和路径的交点数量是奇数则认为点在内部。
如上图(不知道找什么画图软件好,就用了 Sketch ),如果使用 kCAFillRuleNonZero 规则,则该射线和两条路径相交,并且交点方向都是逆时针,所以点在路径内。
如果使用 kCAFillRuleEvenOdd 规则,则该射线与路径有两个交点,为偶数,所以该点不在范围内。
总结
CAShapeLayer 基本属性不多,主要还是需要通过不断的实践,结合贝塞尔曲线来实现不同的需求。
代码及相关资料
PS
第一次上简书首页,欢迎关注,长期更新 iOS 开发相关文章~~~