由于項(xiàng)目需要一個(gè)環(huán)形漸變進(jìn)度條顯示課程,這方便網(wǎng)上的確有很多相關(guān)資料但是,都是比較零散的而且,大多數(shù)只是放一堆代碼就算完了。這里我想詳細(xì)寫一篇我自己實(shí)現(xiàn)這個(gè)進(jìn)度條的過程。
實(shí)現(xiàn)一個(gè)圓弧進(jìn)度條主要分為三步
一、畫圓弧這里用的貝賽爾曲線,就是這個(gè)東西:UIBezierPath
二、根據(jù)貝塞爾曲線路徑畫兩個(gè)圓弧一個(gè)底色一個(gè)上面的填充色,用到的是這個(gè)類CAShapeLayer.h
三、畫兩個(gè)漸變色塊,把上面的進(jìn)度條路徑映射到漸變色塊上,漸變色塊用的是這個(gè)東西CAGradientLayer.h
目標(biāo)效果如圖

第一步:
- 我們把圖案分解開來,就是一個(gè)圓環(huán)上面疊加一個(gè)圓環(huán),底色的圓環(huán)是灰色,而上面的圓環(huán)是自定義顏色的。
-
首先我們畫一個(gè)圓環(huán),其實(shí)就是一個(gè)粗的圓形,這里用UIBezierPath來畫。
效果圖片:
黑色圓環(huán)路徑.jpg
代碼:
//貝塞爾曲線畫圓弧 UIBezierPath *circlePath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.width / 2, self.height / 2) radius:(self.width - 20)/2 startAngle:0 endAngle:2 * M_PI clockwise:YES]; //設(shè)置顏色 [[UIColor blackColor] set]; //線粗細(xì) circlePath.lineWidth = 10; //開始繪圖 [circlePath stroke]; -
這里主要要注意這個(gè)方法:
+ (instancetype)bezierPathWithArcCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise;
五個(gè)參數(shù)分別為
center:圓心
radius:半徑
startAngle:起始角度
endAngle:終止角度
clockwise:是否順時(shí)針畫圖
主要難理解的是startAngle 和 endAngle

所以我要畫出目標(biāo)圓弧就需要從 3π/4 開始到 1π/4結(jié)束
方法改為:
UIBezierPath *circlePath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.width / 2, self.height / 2) radius:(self.width - 20)/2 startAngle:M_PI / 4 + M_PI / 2 endAngle:M_PI / 4 clockwise:YES];
效果圖:

第二步:
- 根據(jù)第一步畫好的路徑通過CAShapeLayer 畫到layer層上去
先畫灰色一圈:
CAShapeLayer *bgLayer = [CAShapeLayer layer];
bgLayer.frame = self.bounds;
bgLayer.fillColor = [UIColor clearColor].CGColor;//填充色 - 透明
bgLayer.lineWidth = 20.f;
bgLayer.strokeColor = ZCCRGBColor(212, 212, 212, 1.0).CGColor;//線條顏色
bgLayer.strokeStart = 0;//起始點(diǎn)
bgLayer.strokeEnd = 1;//終點(diǎn)
bgLayer.lineCap = kCALineCapRound;//讓線兩端是圓滑的狀態(tài)
bgLayer.path = circlePath.CGPath;//這里就是把背景的路徑設(shè)為之前貝塞爾曲線的那個(gè)路徑
[self.layer addSublayer:bgLayer];
效果圖:

- 畫出進(jìn)度條圓弧
_shapeLayer = [CAShapeLayer layer];
_shapeLayer.frame = self.bounds;
_shapeLayer.fillColor = [UIColor clearColor].CGColor;
_shapeLayer.lineWidth = 20.f;
_shapeLayer.lineCap = kCALineCapRound;
// _shapeLayer.strokeColor = color.CGColor;
_shapeLayer.strokeColor = [UIColor blueColor].CGColor;
_shapeLayer.strokeStart = 0;
_shapeLayer.strokeEnd = 0.8;
_shapeLayer.path = circlePath.CGPath;
[self.layer addSublayer:_shapeLayer];
代碼和上面大致一樣就是改一下顏色還有 strokenEnd屬性 讓進(jìn)度不是全滿的,效果圖如下:
第三步:
以上完成了基礎(chǔ)的兩步?,F(xiàn)在就是最麻煩的漸變色這一塊了。首先熟悉下處理漸變色的那個(gè)layer類,CAGradientLayer這個(gè)也是layer的子類,我這直接那例子講吧
//初始化一個(gè)漸變圖層
CAGradientLayer *leftGradientLayer = [CAGradientLayer layer];
//設(shè)frame
leftGradientLayer.frame = CGRectMake(0, 0, self.width / 2, self.height);
//設(shè)漸變顏色
//ZCCRGBColor是我自定義的宏 #define ZCCRGBColor(a,b,c,al) [UIColor colorWithRed:a/255.0 green:b/255.0 blue:c/255.0 alpha:al]
[leftGradientLayer setColors:[NSArray arrayWithObjects:(id)ZCCRGBColor(255, 255, 0, 1).CGColor, (id)ZCCRGBColor(255, 0, 0, 1).CGColor, nil]];
//這里設(shè)置漸變色漸變范圍 0到1就是整個(gè)leftGradientLayer上都在漸變
[leftGradientLayer setLocations:@[@0,@1]];
//下面這兩個(gè)就是漸變色方向Y越大就是越下面 所以是從下到上從黃到紅漸變
[leftGradientLayer setStartPoint:CGPointMake(0, 1)];
[leftGradientLayer setEndPoint:CGPointMake(0, 0)];
添加到父圖層
[_gradientLayer addSublayer:leftGradientLayer];
看一下效果圖
修改下這個(gè)方法
[leftGradientLayer setLocations:@[@0,@0.5]];
效果圖:

只有一邊漸變上半部分全紅。
再修改下這個(gè)屬性 主要控制漸變色方向
[leftGradientLayer setLocations:@[@0,@1]];
[leftGradientLayer setStartPoint:CGPointMake(0, 1)];
[leftGradientLayer setEndPoint:CGPointMake(1, 0)];
這樣就是對(duì)角線的漸變 效果圖如下:

所以簡單點(diǎn)就是這樣做漸變色的環(huán) 先設(shè)置寬高和環(huán)一樣如下圖

然后再加下面這條代碼 就能把漸變色圖層顏色映射到環(huán)的layer上面
[self.gradientLayer setMask:_shapeLayer];
效果圖

但是這里其實(shí)有個(gè)問題,當(dāng)圓弧進(jìn)度滿的時(shí)候就能看到如下圖:

可以看到上面圖片的右下角并沒有那么紅所以不是真正的漸變。那么到底如何做到真正的漸變圓環(huán)呢?
其實(shí)上面剛開始畫漸變圖層的時(shí)候我就埋了個(gè)伏筆。


到這里其實(shí)還有個(gè)問題就是頂部過度會(huì)有一個(gè)明顯的斷層
所以我們就要用到[leftGradientLayer setLocations:@[@0,@0.5]];這個(gè)屬性了 設(shè)置漸變色范圍。讓頂部漸變色基本不動(dòng)
左邊的漸變色塊:
[leftGradientLayer setLocations:@[@0,@0.9]];
右邊的漸變色塊:
[rightGradientLayer setLocations:@[@0.1, @1]];
再看看效果圖

好了 這就差不多完成一個(gè)漸變色圓弧了,如果強(qiáng)迫癥的朋友其實(shí)可以弄四個(gè)漸變色塊從 左下→左上→右上→右下 依次漸變?cè)儆成?,那樣就?huì)更加完美。
終于寫完了。。新手第一次寫這樣的教學(xué)文 有什么錯(cuò)誤的地方還請(qǐng)多包涵指出,不懂得可以留言問我
最后,動(dòng)畫是怎么實(shí)現(xiàn)的呢??
是通過timer 設(shè)置_shapeLayer.strokeEnd這個(gè) strokEnd屬性實(shí)現(xiàn)的
以下是具體代碼~
- (void)animateToProgress:(CGFloat)progress{
// NSLog(@"增加到progress%lf", progress);
if(_shapeLayer.strokeEnd != 0){
[self animateToZero];
}
__weak typeof(self)weakSelf = self;
NSLog(@"-----%lf",_shapeLayer.strokeEnd);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_shapeLayer.strokeEnd * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[weakSelf deleteTimer];
NSString *progressStr = [NSString stringWithFormat:@"%lf",progress];
NSDictionary *userInfo = @{@"progressStr":progressStr};
weakSelf.timer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:weakSelf selector:@selector(animate:) userInfo:userInfo repeats:YES];
});
}
- (void)animate:(NSTimer *)time{
CGFloat progress = [[time.userInfo objectForKey:@"progressStr"] floatValue];
if(_shapeLayer.strokeEnd <= progress)
{
_shapeLayer.strokeEnd += 0.01;
}else{
[self deleteTimer];
}
}
//回滾到0 先判斷 timer 有沒有存在 存在 就把timer 刪除
- (void)animateToZero{
// NSLog(@"刪除到0");
[self deleteTimer];
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selector(animateReset) userInfo:nil repeats:YES];
}
- (void)animateReset{
if(_shapeLayer.strokeEnd > 0){
_shapeLayer.strokeEnd -= 0.01;
}else{
[self deleteTimer];
}
}
- (void)deleteTimer{
[self.timer invalidate];
self.timer = nil;
}
