使用CAShapeLayer繪圖


 

之前講過使用UIBezierPath在UIView的drawRect中繪圖, 今天我們講下另外一種方式: CAShaperLayer

先說說使用CAShapeLayer的優點: GPU執行, GPU執行GPU執行

 

比如我們要畫這樣一個形狀,

 

按照之前的思路是創建一個UIView子類, 用UIBezierPath畫一個外圍的不閉合圓弧, 在畫中間點圓

代碼量不是很多彈也不少, 那假如用CAShapeLayer實現時怎么樣子的呢?

我們先上代碼:

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    
    [super viewDidLoad];
    
    //定義一個CAShapeLayer
    CAShapeLayer *myShapeLayer = ({
    
        //初始化一個實例對象
        CAShapeLayer *circle = [CAShapeLayer layer];
        
        circle.bounds        = CGRectMake(0, 0, 100, 100);  //設置大小
        circle.position      = self.view.center;            //設置中心位置
        circle.path          = \
        [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, 100, 100)].CGPath; //設置繪制路徑
        circle.strokeColor   = [UIColor redColor].CGColor;      //設置划線顏色
        circle.fillColor     = [UIColor yellowColor].CGColor;   //設置填充顏色
        circle.lineWidth     = 10;          //設置線寬
        circle.lineCap       = @"round";    //設置線頭形狀
        circle.strokeEnd     = 0.75;        //設置輪廓結束位置
        
        circle;
    });
    
    //以subLayer的形式添加給self.view
    [self.view.layer addSublayer:myShapeLayer];
}

@end

 

你沒看錯, 就是這么簡單 甚至不用創建UIView子類

我們講下幾個重要屬性:

path

可以看到,這里用的是UIBezierPath生成一個path,然后取他的CGPath來獲取路徑的。他是什么呢?

一層對CGPath的封裝,他更符合OC面向對象的語法風格。這都不是重點。重點是這里有一個初學者經常會犯的錯誤

同學們在繪制曲線的時候經常會以layer在父圖層中的相對位置去繪制曲線,這是錯的!!!

應該以layer自身的坐標系划線。別不當回事,你錯的時候就知道咋回事了😈

另外,如下圖所示,整個圓形UIBezierPath其實是分為多個子路徑繪制的

 

strokeEnd

是輪廓終點的屬性,取值范圍[0,1]。代表輪廓終點在整條路徑的百分比處,相應的還有strokeStart屬性。

不過你應該思考的是:

首先,哪個是所謂的終點靠上的那個點是終點。那為什么0.75是在那個位置呢?請記住,在iOS中,以x軸正方向(即水平向右)為0度,順時針旋轉一周為360度。

 

下面我們再使用CAShapeLayer繪制一些特殊的形狀

 

比如hud這個, 我們之前用UIBezierPath在UIView的DrawRect中畫畫過, 相對比較簡單

我們用CAShapeLayer事實看。

思路:

一個圓角正方形 + 一個空心圓 + 里面的圓弧

上代碼, 重要方法都有注解

    
    CAShapeLayer *layer = ({
    
        CGRect rect = CGRectMake(0, 0, 100, 100);
        
        //創建矩形圓角正方形路徑
        UIBezierPath * rectP   = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:5];
        
        //創建圓路徑
        UIBezierPath * circleP = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(10, 10, 80, 80)];
        
        //內部弧路徑
        UIBezierPath * interP  = [UIBezierPath bezierPathWithArcCenter:CGPointMake(50, 50)
                                                                radius:35
                                                            startAngle:1.5 * M_PI
                                                              endAngle:1.7 * M_PI
                                                             clockwise:NO];
        [interP addLineToPoint:CGPointMake(50, 50)];
        [interP closePath];
        
        //合體
        [rectP appendPath:circleP];
        [rectP appendPath:interP];
        
        CAShapeLayer * layer = [CAShapeLayer layer];
        layer.bounds         = CGRectMake(0, 0, 100, 100);
        layer.position       = self.view.center;
        layer.path           = rectP.CGPath;
        layer.fillColor      = [UIColor colorWithWhite:0 alpha:0.5].CGColor;
        layer.fillRule       = kCAFillRuleEvenOdd;  //重點, 填充規則
        
        layer;
    });
    
    [self.view.layer addSublayer:layer];

 

下面我們再畫這樣一個圓形進度條

直接上代碼

#import "ViewController.h"

@interface ViewController ()

@property (nonatomic, assign)  CGFloat  end;
@property (nonatomic, strong)  CAShapeLayer *mylayer;
@property (nonatomic, strong)  CADisplayLink *displayLink;
@end

@implementation ViewController

- (void)viewDidLoad {
    
    [super viewDidLoad];

    self.mylayer = ({

        //創建圓路徑
        UIBezierPath * circleP = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(10, 10, 80, 80)];
        
        
        CAShapeLayer * layer = [CAShapeLayer layer];
        layer.bounds         = CGRectMake(0, 0, 100, 100);
        layer.position       = self.view.center;
        layer.path           = circleP.CGPath;
        layer.strokeColor    = [[UIColor redColor] colorWithAlphaComponent:0.5].CGColor;
        layer.lineWidth      = 1;
        layer.strokeStart    = 0;
        layer.strokeEnd      = 0;
        layer.fillColor      = [UIColor clearColor].CGColor;
        layer.fillRule       = kCAFillRuleEvenOdd;
        
        layer;
    });
    
    [self.view.layer addSublayer:self.mylayer];
    
    self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(changeEnd)];
    [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];

}

- (void)changeEnd {
    
    self.end = self.end + 0.01;
    self.mylayer.strokeEnd = self.end;
    if (self.end == 1) {
        
        [self.displayLink invalidate];
    }
}

@end

 

我們再來看看這個動畫效果

思路是在綠色的CALayer上面放一個紅色的CAShapeLayer, 然后逐漸增加CAShapeLayer的填色大小

上代碼:

#import "ViewController.h"

static CGFloat count;

@interface ViewController ()

@property (nonatomic, strong) CADisplayLink *displayLink;
@property (nonatomic, strong) CALayer       *greenLayer;
@property (nonatomic, strong) CAShapeLayer  *redLayer;

@end

@implementation ViewController

- (void)viewDidLoad {
    
    [super viewDidLoad];
    
    self.greenLayer = ({
    
        CALayer *layer        = [CALayer layer];
        layer.bounds          = CGRectMake(0, 0, 200, 45);
        layer.position        = self.view.center;
        layer.backgroundColor = [UIColor greenColor].CGColor;
        
        layer;
    });
    
    self.redLayer = ({
    
        CAShapeLayer *layer   = [CAShapeLayer layer];
        layer.bounds          = CGRectMake(0, 0, 200, 45);
        layer.position        = self.view.center;
        layer.path            = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, count / 6 * 2, 45)].CGPath;
        layer.fillColor       = [UIColor redColor].CGColor;
        layer.fillRule        = kCAFillRuleEvenOdd;
        
        layer;
    });
    
    [self.view.layer addSublayer:self.greenLayer];
    [self.view.layer addSublayer:self.redLayer];
    
    self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(action)];
    [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
}

- (void)action {
    
    count ++;
    self.redLayer.path = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, count / 6 * 2, 45)].CGPath;
    
    if (count > 60 * 10 -1) {
        [self.displayLink invalidate];
    }
}

 

這個動畫稍微修改下是不是就可以用作進度指示器了呢

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM