1. 程式人生 > >iOS實現一個顏色漸變的弧形進度條

iOS實現一個顏色漸變的弧形進度條

在Github上看到一些進度條的功能,都是通過Core Graph來實現。無所謂正確與否,但是開發效率明顯就差很多了,而且執行效率還是值得考究的。其實使用蘋果提供的Core Animation能夠非常簡單和方便的實現環形進度條效果,而且還可以高效的保證動畫效果,無論是前進還是後退(語言水平比較有限,就多用程式碼說話)。

1、先來一個結果

80%的狀態:


99%的狀態:


2、需要用到的巨集:

#define degreesToRadians(x) (M_PI*(x)/180.0) //把角度轉換成PI的方式
#define  PROGREESS_WIDTH 80 //圓直徑 
#define PROGRESS_LINE_WIDTH 4 //弧線的寬度

3、CAShapeLayer

   首先,你得要引入Core Animation框架。為了實現環形效果,需要使用到CAShapeLayer,原理是CAShapeLayer可以通過指定Path的方式實現生成一個圖形,非常方便。

4、UIBezierPath

    由於需要畫一個圓形,UIBeziperPath是非常好用的畫圓形的工具。實現下面的程式碼可以畫出上面所示的整個軌道。這個圓形是從-210度的角度到30度。

UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(40, 40) radius:(PROGREESS_WIDTH-PROGRESS_LINE_WIDTH)/2 startAngle:degreesToRadians(-210) endAngle:degreesToRadians(30) clockwise:YES];

5、畫出一個完成的進度的背景軌道

這裡原理很簡單,就是使用CAShapeLayer和UIBezierPath結合起來就能夠達成目標,這一步的結果如下所示:

        _trackLayer = [CAShapeLayer layer];//建立一個track shape layer
        _trackLayer.frame = self.bounds;
        [self.layer addSublayer:_trackLayer];
        _trackLayer.fillColor = [[UIColor clearColor] CGColor];
         _trackLayer.strokeColor = [_strokeColor CGColor];//指定path的渲染顏色
        _trackLayer.opacity = 0.25; //背景同學你就甘心做背景吧,不要太明顯了,透明度小一點
        _trackLayer.lineCap = kCALineCapRound;//指定線的邊緣是圓的
        _trackLayer.lineWidth = PROGRESS_LINE_WIDTH;//線的寬度
        UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(40, 40) radius:(PROGREESS_WIDTH-PROGRESS_LINE_WIDTH)/2 startAngle:degreesToRadians(-210) endAngle:degreesToRadians(30) clockwise:YES];//上面說明過了用來構建圓形
        _trackLayer.path =[path CGPath]; //把path傳遞給layer,然後layer會處理相應的渲染,整個邏輯和CoreGraph是一致的。


6、漸變進度條:

首先要明確的需求是,我們需要顏色根據百分比從紅色漸變到黃色然後再到藍色。
怎麼實現這個顏色的漸變效果。這裡我們需要使用到CAGradientLayer,CAGradientLayer是一個用來畫顏色漸變的層(如果使用透明的顏色,也就可以做到透明漸變)。我們先用CAGradientLayer做出漸變效果,然後把ShapeLayer作為GradientLayer的Mask來截取出需要的部分,以此達到漸變的進度條效果。

    首先,需要構建出順著弧形的顏色漸變。上面的需求我們可以分解成兩部分。
    ①左半部分,顏色從紅色漸變到黃色。
    ②右半部分,顏色從黃色漸變到藍色。
    由此可以瞭解到是我們需要兩個CAShapeLayer。
    為什麼要這麼折騰?CAShapeLayer不能順著弧線進行漸變只能指定兩個點之間進行漸變。所以只能曲線救國了。
    先看看這個部分的效果:

    

    然後,建立一個新的CAShapeLayer來擷取這個顏色漸變的層。
    這部分程式碼如下所示:

        _progressLayer = [CAShapeLayer layer];
        _progressLayer.frame = self.bounds;
        _progressLayer.fillColor =  [[UIColor clearColor] CGColor];
        _progressLayer.strokeColor  = [PROCESS_COLOR CGColor];
        _progressLayer.lineCap = kCALineCapRound;
        _progressLayer.lineWidth = PROGRESS_LINE_WIDTH;
        _progressLayer.path = [path CGPath];
        _progressLayer.strokeEnd = 0;
        
        CALayer *gradientLayer = [CALayer layer];
        CAGradientLayer *gradientLayer1 =  [CAGradientLayer layer];
        gradientLayer1.frame = CGRectMake(0, 0, self.width/2, self.height);
        [gradientLayer1 setColors:[NSArray arrayWithObjects:(id)[[UIColor redColor] CGColor],(id)[UIColorFromRGB(0xfde802) CGColor], nil]];
        [gradientLayer1 setLocations:@[@0.5,@0.9,@1 ]];
        [gradientLayer1 setStartPoint:CGPointMake(0.5, 1)];
        [gradientLayer1 setEndPoint:CGPointMake(0.5, 0)];
        [gradientLayer addSublayer:gradientLayer1];
        
        CAGradientLayer *gradientLayer2 =  [CAGradientLayer layer];
        [gradientLayer2 setLocations:@[@0.1,@0.5,@1]];
        gradientLayer2.frame = CGRectMake(self.width/2, 0, self.width/2, self.height);
        [gradientLayer2 setColors:[NSArray arrayWithObjects:(id)[UIColorFromRGB(0xfde802) CGColor],(id)[MAIN_BLUE CGColor], nil]];
        [gradientLayer2 setStartPoint:CGPointMake(0.5, 0)];
        [gradientLayer2 setEndPoint:CGPointMake(0.5, 1)];
        [gradientLayer addSublayer:gradientLayer2];
        


        [gradientLayer setMask:_progressLayer]; //用progressLayer來擷取漸變層
        [self.layer addSublayer:gradientLayer];

7、進度條效果

        走到上面一步我們得到的效果是一個進度為100%的效果,_progressLayer的長度和_trackLayer的長度是一樣的。那麼怎麼解決百分比的問題呢?

        CAShapeLayer有一個strokeEnd的屬性,這個屬性是從0到1的浮點型別,正好可以用表達百分比,而且這個屬性是animatable,可以動態的表示進度的變化。
如下程式碼所示:

-(void)setPercent:(NSInteger)percent animated:(BOOL)animated
{
    [CATransaction begin];
    [CATransaction setDisableActions:!animated];
    [CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]];
    [CATransaction setAnimationDuration:MAIN_SCREEN_ANIMATION_TIME];
    _progressLayer.strokeEnd = percent/100.0;
    [CATransaction commit];
    
    _percent = percent;
}

8、總結

①進度條的百分比是通過CAShapeLayer的strokeEnd屬性來實現。
②環形的漸變進度條,通過結合CAShapeLayer和CAGradientLayer實現,注意layer的mask屬性的使用。


原文連結:http://www.ganlvji.com/gradient_circle_progress/

2、