之前講過使用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]; } }
這個動畫稍微修改下是不是就可以用作進度指示器了呢