iOS圓弧漸變進度條的實現

由于項目需要一個環形漸變進度條顯示課程,這方便網上的確有很多相關資料但是,都是比較零散的而且,大多數只是放一堆代碼就算完了。這里我想詳細寫一篇我自己實現這個進度條的過程。

實現一個圓弧進度條主要分為三步
一、畫圓弧這里用的貝賽爾曲線,就是這個東西:UIBezierPath
二、根據貝塞爾曲線路徑畫兩個圓弧一個底色一個上面的填充色,用到的是這個類CAShapeLayer.h
三、畫兩個漸變色塊,把上面的進度條路徑映射到漸變色塊上,漸變色塊用的是這個東西CAGradientLayer.h

目標效果如圖


AirPlay_Movie_2017-11-29_04-42-48.gif

第一步:

  • 我們把圖案分解開來,就是一個圓環上面疊加一個圓環,底色的圓環是灰色,而上面的圓環是自定義顏色的。
    • 首先我們畫一個圓環,其實就是一個粗的圓形,這里用UIBezierPath來畫。
      效果圖片:


      黑色圓環路徑.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];
      
      //設置顏色
      [[UIColor blackColor] set];
      
      //線粗細
      circlePath.lineWidth = 10;
      
      //開始繪圖
      [circlePath stroke];
    
    

這里主要要注意這個方法:

+ (instancetype)bezierPathWithArcCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise;

五個參數分別為
center:圓心
radius:半徑
startAngle:起始角度
endAngle:終止角度
clockwise:是否順時針畫圖
主要難理解的是startAngle 和 endAngle

在OC的官方文檔中這樣規定:
官方文檔畫圓弧.jpg

所以我要畫出目標圓弧就需要從 3π/4 開始到 1π/4結束
方法改為:

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];

效果圖:


黑色圓弧路徑.jpg

第二步:

  • 根據第一步畫好的路徑通過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;//起始點
    bgLayer.strokeEnd = 1;//終點 
    bgLayer.lineCap = kCALineCapRound;//讓線兩端是圓滑的狀態
    bgLayer.path = circlePath.CGPath;//這里就是把背景的路徑設為之前貝塞爾曲線的那個路徑
    [self.layer addSublayer:bgLayer];

效果圖:


弧形進度條底色.jpg
  • 畫出進度條圓弧
_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屬性 讓進度不是全滿的,效果圖如下:
藍色進度條.jpg

第三步:
以上完成了基礎的兩步?,F在就是最麻煩的漸變色這一塊了。首先熟悉下處理漸變色的那個layer類,CAGradientLayer這個也是layer的子類,我這直接那例子講吧

//初始化一個漸變圖層
    CAGradientLayer *leftGradientLayer = [CAGradientLayer layer];
//設frame
    leftGradientLayer.frame = CGRectMake(0, 0, self.width / 2, self.height);
//設漸變顏色 
//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]];
//這里設置漸變色漸變范圍 0到1就是整個leftGradientLayer上都在漸變
    [leftGradientLayer setLocations:@[@0,@1]];
//下面這兩個就是漸變色方向Y越大就是越下面 所以是從下到上從黃到紅漸變
    [leftGradientLayer setStartPoint:CGPointMake(0, 1)];
    [leftGradientLayer setEndPoint:CGPointMake(0, 0)];
添加到父圖層
    [_gradientLayer addSublayer:leftGradientLayer];

看一下效果圖
左邊從下到上漸變圖層.jpg

修改下這個方法

 [leftGradientLayer setLocations:@[@0,@0.5]];

效果圖:


只有一半是漸變的.jpg

只有一邊漸變上半部分全紅。

再修改下這個屬性 主要控制漸變色方向

     [leftGradientLayer setLocations:@[@0,@1]];
    [leftGradientLayer setStartPoint:CGPointMake(0, 1)];
    [leftGradientLayer setEndPoint:CGPointMake(1, 0)];

這樣就是對角線的漸變 效果圖如下:


對角線漸變.jpg

所以簡單點就是這樣做漸變色的環 先設置寬高和環一樣如下圖


漸變色整塊.jpg

然后再加下面這條代碼 就能把漸變色圖層顏色映射到環的layer上面

[self.gradientLayer setMask:_shapeLayer];

效果圖


對角線漸變色圓弧.jpg

但是這里其實有個問題,當圓弧進度滿的時候就能看到如下圖:


對角線漸變圓弧全.jpg

可以看到上面圖片的右下角并沒有那么紅所以不是真正的漸變。那么到底如何做到真正的漸變圓環呢?

其實上面剛開始畫漸變圖層的時候我就埋了個伏筆。

而且煞費苦心的測試了漸變圖層的那幾個屬性,其實就是為了如下的效果:
左右漸變圖層.jpg

然后映射到圓弧上如下效果圖:
左右漸變塊映射的圓弧.jpg

到這里其實還有個問題就是頂部過度會有一個明顯的斷層
所以我們就要用到[leftGradientLayer setLocations:@[@0,@0.5]];這個屬性了 設置漸變色范圍。讓頂部漸變色基本不動

左邊的漸變色塊:
[leftGradientLayer setLocations:@[@0,@0.9]];
右邊的漸變色塊:
[rightGradientLayer setLocations:@[@0.1, @1]];

再看看效果圖


圖片.png

好了 這就差不多完成一個漸變色圓弧了,如果強迫癥的朋友其實可以弄四個漸變色塊從 左下→左上→右上→右下 依次漸變再映射,那樣就會更加完美。

終于寫完了。。新手第一次寫這樣的教學文 有什么錯誤的地方還請多包涵指出,不懂得可以留言問我

最后,動畫是怎么實現的呢??
是通過timer 設置_shapeLayer.strokeEnd這個 strokEnd屬性實現的
以下是具體代碼~

- (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;
}

完整代碼gitHub鏈接

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容