最終效果大概是這樣滴堪伍,動畫要求是時長共兩秒。
第一秒進度條滑動至進度的90%觅闽,第二秒滑動剩下的10%帝雇,中間數字跟隨滑動顯示當前已滑動的百分比。
基本思路:
1.繪制貝塞爾曲線蛉拙,畫圓尸闸;
2.創(chuàng)建底部灰色部分圓環(huán);
3.創(chuàng)建進度條圓環(huán)孕锄;
4.添加漸變色圖層吮廉;
5.設置定時器開始動畫;
文中一些參數說明
#define percent 0.9 //第一段動畫完成百分比
#define duration_First 1.0 //第一段動畫時長
#define duration_Second 1.0 //第二段動畫時長
#define TimeInterval 0.02 //定時器間隔時間
@property (assign, nonatomic) CGFloat progress;//進度值
一畸肆、繪制貝塞爾曲線宦芦,畫圓
//貝塞爾曲線畫圓弧
UIBezierPath *circlePath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.width/2, self.height/2)radius:(self.width-kAdjustRatio(17))/2.0 startAngle:-M_PI/2 endAngle:3*M_PI/2 clockwise:YES];
//設置顏色
[[UIColor whiteColor] set];
circlePath.lineWidth = 10;
//開始繪圖
[circlePath stroke];
關于貝塞爾曲線參數說明:
1.Center:圓心坐標;
2.radius:圓半徑轴脐,以上圖為例调卑,即為中間圓心到圓環(huán)中間部分的距離;
3.startAngle:畫圓的開始角度豁辉;
4.endAngle:畫圓的結束角度令野;
5.clockwise:是否為順時針,YES是徽级,NO否气破;
由于iOS默認的角度分布如下圖所示,起始0是在右邊餐抢,所以想要從頂部順時針開始畫圓现使,起始終止角度應該是-M_PI/2和3M_PI/2
二、創(chuàng)建底部灰色部分圓環(huán)
圓環(huán)部分用的是CAShapeLayer
CAShapeLayer
屬于QuartzCore
框架旷痕,繼承自CALayer
碳锈。CAShapeLayer
是在坐標系內繪制貝塞爾曲線的,通過繪制貝塞爾曲線欺抗,設置shape(形狀)的path(路徑)售碳,從而繪制各種各樣的圖形以及不規(guī)則圖形。因此,使用CAShapeLayer
需要與UIBezierPath
一起使用贸人。
UIBezierPath
類允許你在自定義的 View 中繪制和渲染由直線和曲線組成的路徑间景。你可以在初始化的時候直接為你的UIBezierPath
指定一個幾何圖形。
通俗點就是UIBezierPath
用來指定繪制圖形路徑艺智,而CAShapeLayer
就是根據路徑來繪圖的倘要。
CAShapeLayer *bgLayer = [CAShapeLayer layer];
bgLayer.frame = self.bounds;
//填充色 透明
bgLayer.fillColor = [UIColor clearColor].CGColor;
bgLayer.lineWidth = 10;
//線條顏色
bgLayer.strokeColor = kUIColorFromRGB(0xF6F6F9).CGColor;
//起始點
bgLayer.strokeStart = 0;
//終點
bgLayer.strokeEnd = 1;
//讓線兩端是圓滑的狀態(tài)
bgLayer.lineCap = kCALineCapRound;
//把背景的路徑設為貝塞爾曲線路徑
bgLayer.path = circlePath.CGPath;
[self.bgView.layer addSublayer:bgLayer];
三、創(chuàng)建進度條圓環(huán)
進度條圓環(huán)依然使用CAShapeLayer
,strokeEnd
暫設為0十拣,在后面添加動畫時動態(tài)調整數值封拧。
_progressLayer = [CAShapeLayer layer];
_progressLayer.frame = self.bounds;
_progressLayer.fillColor = [UIColor clearColor].CGColor;
_progressLayer.lineWidth = 17;
_progressLayer.lineCap = kCALineCapRound;
_progressLayer.strokeColor = kUIColorFromRGB(0xC8A159).CGColor;
_progressLayer.strokeStart = 0;
_progressLayer.strokeEnd = 0;
_progressLayer.path = circlePath.CGPath;
[self.layer addSublayer:_progressLayer];
四、添加漸變色圖層
漸變圖層使用CAGradientLayer
,是用于處理漸變色的圖層夭问,一樣繼承自CALayer
泽西。
_gradientLayer = [CAGradientLayer layer];
_gradientLayer.frame = self.bounds;
[self.bgView.layer addSublayer:_gradientLayer];
由于CAGradientLayer
是線性漸變的,所以在這里甲喝,我在此圖層上加了兩個子圖層用以做圓環(huán)的漸變處理尝苇,
//左漸變圖層
CAGradientLayer *leftGradientLayer = [CAGradientLayer layer];
leftGradientLayer.frame = CGRectMake(0, 0, self.width/2, self.height);
[leftGradientLayer setColors:[NSArray arrayWithObjects:(id)kUIColorFromRGB(0xEBD6AB).CGColor, (id)kUIColorFromRGB(0xC6A05D).CGColor, nil]];
[leftGradientLayer setLocations:@[@0.0,@0.9]];
[leftGradientLayer setStartPoint:CGPointMake(0, 0)];
[leftGradientLayer setEndPoint:CGPointMake(0, 1)];
[_gradientLayer addSublayer:leftGradientLayer];
//右漸變圖層
CAGradientLayer *rightGradientLayer = [CAGradientLayer layer];
rightGradientLayer.frame = CGRectMake(self.width/2, 0, self.width/2, self.height);
[rightGradientLayer setColors:[NSArray arrayWithObjects:(id)kUIColorFromRGB(0xEBD6AB).CGColor, (id)kUIColorFromRGB(0xC6A05D).CGColor, nil]];
[rightGradientLayer setLocations:@[@0.1,@1.0]];
[rightGradientLayer setStartPoint:CGPointMake(0, 0)];
[rightGradientLayer setEndPoint:CGPointMake(0, 1)];
[_gradientLayer addSublayer:rightGradientLayer];
[_gradientLayer setMask:_progressLayer];
參數說明
- colors:起始顏色數組铛只,至少兩個,可以多個埠胖;
- locations:定義每種顏色的位置,一個
NSNumber
數組淳玩,數量對應colors直撤,取值范圍[0,1],值必須為單調遞增的蜕着,例如[@0.2,@0.5,@1]谋竖;startPoint,endPoint:顏色起始和結束點,也就是顏色漸變的方向承匣,如下圖所示蓖乘;
Point的x,y值分別代表X,Y方向韧骗,x值越大代表越靠右嘉抒,y值越大越往下,例如
(0,0)->(0,1)代表豎直(Y)方向從上往下漸變袍暴,
(0,0)->(1,0)代表水平(X)水平方向從左至右漸變些侍,
(1,0)->(0,1)代表從右上角到左下角的漸變;
為了漸變效果更好政模,也可以把漸變區(qū)域分為四塊岗宣,也就是四個CAGradientLayer
,顏色首尾相接淋样,拼成一個完整的漸變圓形耗式,本文只做了左右兩部分,都是從上至下的顏色漸變,這里不再舉例細說刊咳。
五措嵌、設置定時器開始動畫
由于要數字跟隨動畫效果實時改變數字,所以選擇了定時器芦缰,一般的沒有此要求的動畫可以直接用CABasicAnimation
動畫做就可以了企巢。
因為有兩段動畫,所以這里是先執(zhí)行一段動畫让蕾,再延時第一段動畫時間后浪规,執(zhí)行第二段動畫。
//第一段動畫
[self point];
//開啟計時器
[self startAnimate];
WS(weakSelf);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(duration_First * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//第二段動畫
[weakSelf secondPoint];
});
進度條的動畫時采用CABasicAnimation
動畫探孝,而效果圖上那個小白點的動畫效果時在滑動中始終在進度條的頭部位置跟隨滑動笋婿,即為一個視圖沿曲線圓運動這么一個效果,在本文中就是沿著和進度條共同的貝塞爾曲線圓運動顿颅,采用的是CAKeyframeAnimation
動畫缸濒。
- (void)point{
CABasicAnimation *animation_1 = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
animation_1.fromValue = @0;
animation_1.toValue = [NSNumber numberWithDouble:self.progress*percent];
animation_1.duration = duration_First;
animation_1.fillMode = kCAFillModeForwards;
animation_1.removedOnCompletion = NO;
[self.progressLayer addAnimation:animation_1 forKey:nil];
CAKeyframeAnimation *pathAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
pathAnimation.calculationMode = kCAAnimationPaced;
pathAnimation.fillMode = kCAFillModeForwards;
pathAnimation.removedOnCompletion = NO;
pathAnimation.duration = duration_First;
pathAnimation.repeatCount = 0;
UIBezierPath *circlePath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.width/2, self.height/2) radius:(self.width-kAdjustRatio(17))/2.0 startAngle:-M_PI/2 endAngle:-M_PI/2+2*M_PI*self.progress*percent clockwise:YES];
pathAnimation.path = circlePath.CGPath;
[self.pointView.layer addAnimation:pathAnimation forKey:@"movePoint"];
}
參數說明
keyPath:動畫執(zhí)行的屬性值,可以執(zhí)行l(wèi)ayer的一些屬性粱腻,改變值形成動畫庇配,這里使用的是CAShapeLayer
的strokeEnd
屬性;
fromValue:動畫起始值绍些;
toValue:動畫結束值捞慌,本文中第一段結束值為進度的90%,所以為self.progress*percent
柬批;
duration:動畫時長啸澡;
fillMode:視圖在非Active時的行為,kCAFillModeForwards為始終保持為最新狀態(tài)氮帐;
removedOnCompletion:動畫完成后是否刪除動畫效果嗅虏;
啟動定時器
- (void)startAnimate{
[self deleteTimer];
self.timer = [NSTimer scheduledTimerWithTimeInterval:TimeInterval target:self selector:@selector(animate:) userInfo:nil repeats:YES];
}
//刪除定時器
- (void)deleteTimer{
[self.timer invalidate];
self.timer = nil;
}
定時器逐漸增加self.progressLab
要顯示的值self.showProgress
,要在1秒鐘之內將值從0增加至進度的90%上沐,假設進度為0.8皮服,則self.showProgress
要從1秒內從0漸增至0.8*0.9,定時器每TimeInterval
(0.02秒)執(zhí)行一次奄容,則每一次執(zhí)行要增加的值為...emmmmm...(0.8x0.9)÷(1÷0.02)冰更,嗯,就是這個昂勒,后面一段增加同理蜀细。
- (void)animate:(NSTimer *)time{
if (self.showProgress <= self.progress*percent) {
self.showProgress += TimeInterval*self.progress*percent/duration_First;
}else if (self.showProgress <= self.progress){
self.showProgress += TimeInterval*self.progress*(1-percent)/duration_Second;
}else{
[self deleteTimer];
}
if (self.showProgress > 1) {
self.showProgress = 1;
}
NSString *progressStr = [NSString stringWithFormat:@"%.0f%%",self.showProgress*100];
self.progressLab.text = progressStr;
}
第二段動畫,跟第一段動畫同樣的方法
特別注意兩段動畫的取值范圍戈盈,第一奠衔、二段
CAKeyframeAnimation
動畫的貝塞爾曲線的startAngle
和endAngle
谆刨,都根據要滑動的范圍做了處理,不再是整圓了归斤,而是每段進度要滑動的圓弧的角度痊夭。
- (void)secondPoint{
CABasicAnimation *animation_1 = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
animation_1.fromValue = [NSNumber numberWithDouble:self.progress*percent];
animation_1.toValue = [NSNumber numberWithDouble:self.progress];
animation_1.duration = duration_Second;
animation_1.fillMode = kCAFillModeForwards;
animation_1.removedOnCompletion = NO;
[self.progressLayer addAnimation:animation_1 forKey:nil];
CAKeyframeAnimation *pathAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
pathAnimation.calculationMode = kCAAnimationPaced;
pathAnimation.fillMode = kCAFillModeForwards;
pathAnimation.removedOnCompletion = NO;
pathAnimation.duration = duration_Second;
pathAnimation.repeatCount = 0;
UIBezierPath *circlePath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.width/2, self.height/2) radius:(self.width-kAdjustRatio(17))/2.0 startAngle:-M_PI/2+2*M_PI*self.progress*percent endAngle:-M_PI/2+2*M_PI*self.progress clockwise:YES];
pathAnimation.path = circlePath.CGPath;
[self.pointView.layer addAnimation:pathAnimation forKey:@"movePoint"];
}
一個比較簡單的漸變圓環(huán)進度條就做好了,外部傳入進度progress
脏里,即可開始執(zhí)行動畫她我。也可以自己做一些其他處理,比如加一個按鈕迫横,點擊后動畫再來一遍番舆,again and again...,或者多個圓環(huán)的進度條等等矾踱。
https://github.com/TonyHYH/HYHCircleView.git
補充:關于畫順時針的圓和逆時針的圓
如果要畫順時針的圓恨狈,貝塞爾曲線的起止角度是從-M_PI/2到3M_PI/2,如果要逆時針呛讲,那么這個起止角度就要反過來禾怠,從3M_PI/2到-M_PI/2。另外贝搁,那個小白點運動的貝塞爾曲線的起止角度也要一起改變吗氏,就是第一二段動畫那里的pointView的動畫。