CAShapeLayer 初探

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

image.png

為什么有了 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 屬性??梢宰龀鲆恍┍容^炫酷的圖形動畫效果。

image.png

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];
}

運行效果:

image.png

當然,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;

一個比較常用使用的場景。

image.png

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];
}

運行效果:

image.png

使用 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矩形動畫.gif

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

CAShapeLayer動畫集.gif

可以利用 CAShapeLayer 的動畫特性做一些提示類的動畫。

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

CAShapeLayer 提示類動畫.gif

正確對勾動畫


/**
 先畫一個圈,在畫一個對勾.
 */
- (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:)

UIBezierPath 的 appendPath: 方法

然后把 layerPath 設置給 CAShapeLayer 即可。

CAShapeLayer.path = layerPath.CGPath;

最后總結

  1. CAShapeLayer 繼承自 CALayer。所以,可以使用 CAAnimation 來做動畫。
  2. CAShapeLayer 有一個 path 屬性,可以繪制你能想到的所有的路徑。
  3. CAShapeLayer 搭配 strokeStart & strokeEnd 兩個屬性,可以做出比較炫酷的動畫。

注意點:CAShapeLayer 搭配 UIBezierPath 時,默認的 fillColor 是黑色的。

CAShapeLayer 主要是搭配 UIBezierPath 加上 CAAnimation 配合 strokeStart & strokeEnd 來做出比較炫酷的動畫。

由于時間比較倉促,這片文章只是簡單的介紹了CAShapeLayer的基本使用。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容