CAShapeLayer 是什么?
CAShapeLayer 是一個繼承自 CALayer 的一個子Layer。

為什么有了 CALayer 之后,還要有一個 CAShapeLayer ? 有什么優(yōu)勢嗎?
我們都知道 CALayer,是 UIView 內(nèi)部一個用于顯示的容器。
那 CAShapeLayer 作為另外的一種 Layer,出現(xiàn)的目的是什么的?
個人理解:CAShapeLayer,作為一個 CALayer 它隔離了和 UIView 的強聯(lián)系。
可以單獨的拿過來用。
那僅僅只有這么一點,我們也可以自定義 CALayer 出來用啊。
CAShapeLayer 有一個特別好用的屬性,
path。我們可以里用UIBezierPath&CAShapeLayer的.path屬性,畫出我們?nèi)我庀M@示的圖形。并且搭配 CAShapeLayer 的strokeBegin&strokeEnd屬性??梢宰龀鲆恍┍容^炫酷的圖形動畫效果。

CAShapeLayer 的基本使用。
由于 CAShapeLayer 是繼承自 CALayer 的,那么 CALayer 可以咋用,CAShapeLayer 就可以咋用。
/**
1. CAShaperLayer : CALayer.
2. CALayer 是可以做動畫的,所以 CAShapeLayer 也可以做動畫。
3. CAShapeLayer 是 CALayer ,不是 UIView 所以,不能接受事件。
*/
- (void)cashapeLayer {
CAShapeLayer *shapeLayer = [[CAShapeLayer alloc] init];
shapeLayer.frame = CGRectMake(0, 64, 200, 200);
// 和背景色不沖突?
shapeLayer.backgroundColor = [UIColor orangeColor].CGColor;
// 設置描邊顏色
shapeLayer.strokeColor = [UIColor blackColor].CGColor;
// 設置填充顏色
shapeLayer.fillColor = [UIColor greenColor].CGColor;
// 發(fā)現(xiàn),這兩個屬性并沒什么亂用。
// 類似于描邊、填充等屬性,必須作用在路徑上。
[self.view.layer addSublayer:shapeLayer];
}
特點注意:雖然 CAShapeLayer 可以使用 CALayer 的那套。但是,如果不搭配 UIBezierPath的話,那么對于大多數(shù) CAShapeLayer 特有的屬性來說,都是不起作用的。
CAShapeLayer 搭配 UIBezierPath 來使用。
CAShapeLayer 搭配 UIBezierPath 使用,才是最適合的做法。
我們可以使用 UIBezierPath 繪制出自己需要的圖形,并設置到 CAShapeLayer 上,來達到繪制 CAShapeLayer 的目的。
/**
使用路徑繪制 CAShape
因為用到了 貝塞爾曲線,所以就可以使用核心繪圖的一些參數(shù)。
比如,線寬、描邊、填充等。
*/
- (void)shapeLayerUserPath {
CAShapeLayer *shapeLayer = [[CAShapeLayer alloc] init];
// 創(chuàng)建路徑
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(10, 80)];
[path addLineToPoint:CGPointMake(100, 80)];
[path addLineToPoint:CGPointMake(100, 180)];
[path addLineToPoint:CGPointMake(10, 180)];
[path addLineToPoint:CGPointMake(10, 80)];
// [path closePath]; // 閉合路徑
// 設置 CAShapeLayer 的繪制路徑
shapeLayer.path = path.CGPath;
// 有路徑了,就可以設置填充顏色,線的樣式,描邊顏色等。
shapeLayer.strokeColor = [UIColor purpleColor].CGColor;
// CAShapeLayer 如果是閉合路徑,那么默認的填充顏色是黑色。
// shapeLayer.fillColor = [UIColor orangeColor].CGColor;
shapeLayer.fillColor = [UIColor whiteColor].CGColor;
shapeLayer.lineWidth = 10; // 線寬
shapeLayer.lineJoin = @"round"; // 線頭樣式
shapeLayer.lineCap = @"round"; // 折現(xiàn)結合處樣式
[self.view.layer addSublayer:shapeLayer];
}
運行效果:

當然,UIBezierPath 能夠畫出什么圖形,那么 CAShapeLayer 就能顯示出多少中圖形。
+ (instancetype)bezierPath;
+ (instancetype)bezierPathWithRect:(CGRect)rect;
+ (instancetype)bezierPathWithOvalInRect:(CGRect)rect;
+ (instancetype)bezierPathWithRoundedRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius; // rounds all corners with the same horizontal and vertical radius
+ (instancetype)bezierPathWithRoundedRect:(CGRect)rect byRoundingCorners:(UIRectCorner)corners cornerRadii:(CGSize)cornerRadii;
+ (instancetype)bezierPathWithArcCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise;
+ (instancetype)bezierPathWithCGPath:(CGPathRef)CGPath;
- (instancetype)init NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER;
// Returns an immutable CGPathRef which is only valid until the UIBezierPath is further mutated.
// Setting the path will create an immutable copy of the provided CGPathRef, so any further mutations on a provided CGMutablePathRef will be ignored.
@property(nonatomic) CGPathRef CGPath;
- (CGPathRef)CGPath NS_RETURNS_INNER_POINTER CF_RETURNS_NOT_RETAINED;
// Path construction
- (void)moveToPoint:(CGPoint)point;
- (void)addLineToPoint:(CGPoint)point;
- (void)addCurveToPoint:(CGPoint)endPoint controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2;
- (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint;
- (void)addArcWithCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise NS_AVAILABLE_IOS(4_0);
- (void)closePath;
一個比較常用使用的場景。

ofo 小黃車的 App 界面,界面的下半部分是一個曲線加一個矩形的樣式。
可以利用 CAShapeLayer & UIBezierPath 來實現(xiàn)這么一個場景。
主要思路:在屏幕中間的地方,畫一條曲線即可。
/* |-------------------------------------------------------------------|
| |
| |
| 控制點(controlPoint) |
| |
| |
| 起點(moveToPoint) 終點(toPint) |
-------------------------------------------------------------------
*/
- (void)prepareUI {
CAShapeLayer *layer = [CAShapeLayer layer];
UIBezierPath *path = [UIBezierPath bezierPath];
// 畫一條貝塞爾曲線
[path moveToPoint:CGPointMake(0, [UIScreen mainScreen].bounds.size.height * 0.5 + 100)];
[path addQuadCurveToPoint:CGPointMake([UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height * 0.5 + 100) controlPoint:CGPointMake(self.view.center.x, self.view.center.y )];
// 設置曲線到 CAShapeLayer 的 path
layer.path = path.CGPath;
// 實現(xiàn)線的基本屬性
layer.strokeColor = [UIColor purpleColor].CGColor;
layer.lineWidth = 5;
layer.lineCap = kCALineCapRound;
layer.fillColor = [UIColor whiteColor].CGColor;
[self.view.layer addSublayer:layer];
}
運行效果:

使用 CAShapeLayer 以動畫的方式繪制圖形。
由于 CAShapeLayer 繼承自 CALayer。CALayer 有可以搭配 CAAnimation 使用。
所以可以使用 CAShapeLayer & UIBezierPath & CAAnimation 來產(chǎn)生比較酷炫的動畫效果。
主要是搭配 CAShapeLayer 的 strokeStart & strokeEnd 來實現(xiàn)比較炫酷的效果。
最簡單的矩形動畫
/**
動畫畫矩形
*/
- (void)shapeLayerDrawRect {
CAShapeLayer *layer = [CAShapeLayer layer];
UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(10, 200, 100, 100)];
layer.path = path.CGPath;
layer.strokeColor = [UIColor orangeColor].CGColor;
layer.fillColor = [UIColor whiteColor].CGColor;
layer.lineWidth = 3;
layer.lineCap = kCALineCapRound;
[self.view.layer addSublayer:layer];
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
anim.fromValue = @0;
anim.toValue = @1;
anim.repeatCount = MAXFLOAT;
anim.duration = 3;
anim.fillMode = kCAFillModeForwards;
anim.removedOnCompletion = NO;
[layer addAnimation:anim forKey:nil];
}

基本上,你能畫出的路徑都能做出這些動畫。

可以利用 CAShapeLayer 的動畫特性做一些提示類的動畫。
由于 CAShapeLayer 不是 UIResponder ,所以,它不能接受事件。
它的作用,就是展示,也僅僅是展示。
可以利用 CAShapeLayer 的路徑動畫特性,做一些有功能性的動畫。

正確對勾動畫
/**
先畫一個圈,在畫一個對勾.
*/
- (void)successClick {
// UIView *view = [[UIView alloc] init];
// view.frame = CGRectMake(0, 0, 40, 40);
// view.center = self.view.center;
// view.backgroundColor = [UIColor blackColor];
// [self.view addSubview:view];
CAShapeLayer *successLayer = [CAShapeLayer layer];
//[view.layer addSublayer:successLayer];
UIBezierPath *layerPath = [UIBezierPath bezierPath];
// 原形 path
UIBezierPath *path1 = [UIBezierPath bezierPathWithArcCenter:self.view.center radius:40 startAngle:-M_PI_2 endAngle:M_PI * 2 - M_PI_2 clockwise:YES];
[layerPath appendPath:path1];
// 對勾 path
UIBezierPath *path2 = [UIBezierPath bezierPath];
[path2 moveToPoint:CGPointMake(self.view.center.x - 40, self.view.center.y)];
// [path2 moveToPoint:CGPointMake(147,334)];
[path2 addLineToPoint:CGPointMake(self.view.center.x, self.view.center.y + 40)];
// [path2 addLineToPoint:CGPointMake(186, 372)];
[path2 addLineToPoint:CGPointMake(self.view.center.x + 40 / 1.4, self.view.center.y - 40 / 1.4)];
//[path2 addLineToPoint:CGPointMake(218,309)];
[layerPath appendPath:path2];
successLayer.path = layerPath.CGPath;
successLayer.strokeColor = [UIColor orangeColor].CGColor;
successLayer.lineCap = kCALineCapRound;
successLayer.lineWidth = 3;
successLayer.fillColor = [UIColor whiteColor].CGColor;
// [view.layer addSublayer:successLayer];
CABasicAnimation *ani = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
ani.fromValue = @0;
ani.toValue = @1;
ani.repeatCount = 1;
ani.duration = 2;
ani.fillMode = kCAFillModeForwards;
ani.removedOnCompletion = NO;
ani.delegate = self;
[successLayer addAnimation:ani forKey:@"successAni"];
[self.view.layer addSublayer:successLayer];
_successLayer = successLayer;
}
失敗的 XX 動畫
- (void)failClick {
CAShapeLayer *failLayer = [CAShapeLayer layer];
UIBezierPath *failPathTotal = [UIBezierPath bezierPath];
UIBezierPath *path1 = [UIBezierPath bezierPathWithArcCenter:self.view.center radius:40 startAngle:-M_PI_2 endAngle:M_PI * 2 - M_PI_2 clockwise:YES];
[failPathTotal appendPath:path1];
UIBezierPath *path2 = [UIBezierPath bezierPath];
[path2 moveToPoint:CGPointMake(self.view.center.x - 40 / 1.4, self.view.center.y - 40 / 1.4)];
[path2 addLineToPoint:CGPointMake(self.view.center.x + 40 / 1.4, self.view.center.y + 40 / 1.4)];
[failPathTotal appendPath:path2];
UIBezierPath *path3 = [UIBezierPath bezierPath];
[path3 moveToPoint:CGPointMake(self.view.center.x + 40 / 1.4, self.view.center.y - 40 / 1.4)];
[path3 addLineToPoint:CGPointMake(self.view.center.x - 40 / 1.4, self.view.center.y + 40 / 1.4)];
[failPathTotal appendPath:path3];
failLayer.path = failPathTotal.CGPath;
failLayer.strokeColor = [UIColor orangeColor].CGColor;
failLayer.lineCap = kCALineCapRound;
failLayer.lineWidth = 3;
failLayer.fillColor = [UIColor whiteColor].CGColor;
[self.view.layer addSublayer:failLayer];
CABasicAnimation *ani = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
ani.fromValue = @0;
ani.toValue = @1;
ani.repeatCount = 1;
ani.duration = 2;
ani.fillMode = kCAFillModeBackwards;
ani.removedOnCompletion = NO;
ani.delegate = self;
[failLayer addAnimation:ani forKey:@"failAni"];
_failLayer = failLayer;
}
核心則是,首先需要把動畫需要的幾條路徑計算出來。
比如,第一個 git 路徑包含兩部分。
1.一個原形路徑
// 圓形 path
UIBezierPath *path1 = [UIBezierPath bezierPathWithArcCenter:self.view.center radius:40 startAngle:-M_PI_2 endAngle:M_PI * 2 - M_PI_2 clockwise:YES];
2.一個折線路徑
// 對勾 path
UIBezierPath *path2 = [UIBezierPath bezierPath];
[path2 moveToPoint:CGPointMake(self.view.center.x - 40, self.view.center.y)];
// [path2 moveToPoint:CGPointMake(147,334)];
[path2 addLineToPoint:CGPointMake(self.view.center.x, self.view.center.y + 40)];
// [path2 addLineToPoint:CGPointMake(186, 372)];
[path2 addLineToPoint:CGPointMake(self.view.center.x + 40 / 1.4, self.view.center.y - 40 / 1.4)];
然后把這兩個路徑添加到另外一個 UIBezierPath 中。
UIBezierPath *layerPath = [UIBezierPath bezierPath];
[layerPath appendPath:path1];
[layerPath appendPath:path2];
(對,UIBezierPath不光可以 add 各種形狀,還可以 appendPath:)

然后把 layerPath 設置給 CAShapeLayer 即可。
CAShapeLayer.path = layerPath.CGPath;
最后總結
- CAShapeLayer 繼承自 CALayer。所以,可以使用 CAAnimation 來做動畫。
- CAShapeLayer 有一個 path 屬性,可以繪制你能想到的所有的路徑。
- CAShapeLayer 搭配 strokeStart & strokeEnd 兩個屬性,可以做出比較炫酷的動畫。
注意點:CAShapeLayer 搭配 UIBezierPath 時,默認的 fillColor 是黑色的。
CAShapeLayer 主要是搭配 UIBezierPath 加上 CAAnimation 配合 strokeStart & strokeEnd 來做出比較炫酷的動畫。
由于時間比較倉促,這片文章只是簡單的介紹了CAShapeLayer的基本使用。