利用貝塞爾曲線繪制(UIBezierPath)自定義iOS動態速度表,可以自定義刻度,刻度值,進度條樣式


GitHubDemo下載地址

使用UIBezierPath畫圖步驟:

  1. 創建一個UIBezierPath對象
  2. 調用-moveToPoint:設置初始線段的起點
  3. 添加線或者曲線去定義一個或者多個子路徑

改變UIBezierPath對象跟繪圖相關的屬性。如,我們可以設置畫筆的屬性、填充樣式等

UIBezierPath創建方法介紹

我們先看看UIBezierPath類提供了哪些創建方式,這些都是工廠方法,直接使用即可。

 

+ (instancetype)bezierPath;
+ (instancetype)bezierPathWithRect:(CGRect)rect;
+ (instancetype)bezierPathWithOvalInRect:(CGRect)rect;
+ (instancetype)bezierPathWithRoundedRect:(CGRect)rect
                             cornerRadius:(CGFloat)cornerRadius;
+ (instancetype)bezierPathWithRoundedRect:(CGRect)rect
                        byRoundingCorners:(UIRectCorner)corners
                              cornerRadii:(CGSize)cornerRadii;
+ (instancetype)bezierPathWithArcCenter:(CGPoint)center
                                 radius:(CGFloat)radius
                             startAngle:(CGFloat)startAngle
                               endAngle:(CGFloat)endAngle
                              clockwise:(BOOL)clockwise;
+ (instancetype)bezierPathWithCGPath:(CGPathRef)CGPath;

 

本文主要是利用下面的方法繪制圓形,進而形成動態的速度表盤形式:

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

這個工廠方法用於畫弧,參數說明如下:

  1. center: 弧線中心點的坐標
  2. radius: 弧線所在圓的半徑
  3. startAngle: 弧線開始的角度值
  4. endAngle: 弧線結束的角度值
  5. clockwise: 是否順時針畫弧線
UIBezierPath* outArc=[UIBezierPath bezierPathWithArcCenter:LuCenter radius:self.arcRadius startAngle:startAngle endAngle:endAngle clockwise:YES];
    CAShapeLayer* shapeLayer=[CAShapeLayer layer];
    shapeLayer.lineWidth=lineWitdth;
    shapeLayer.fillColor=filleColor.CGColor;
    shapeLayer.strokeColor=strokeColor.CGColor;
    shapeLayer.path=outArc.CGPath;
    shapeLayer.lineCap=kCALineCapRound;
    [self.layer addSublayer:shapeLayer];

CAShapeLayer有着幾點很重要(使用CAShapeLayer與UIBezierPath可以實現不在view的drawRect方法中就畫出一些想要的圖形):

  1. 它依附於一個給定的path,必須給與path,而且,即使path不完整也會自動首尾相接

  2. strokeStart以及strokeEnd代表着在這個path中所占用的百分比

  3. CAShapeLayer動畫僅僅限於沿着邊緣的動畫效果,它實現不了填充效果

 下面介紹一下速度表盤的實現過程:

  1. 畫外圍弧度
    /**
     *  畫弧度
     *
     *  @param startAngle  開始角度
     *  @param endAngle    結束角度
     *  @param lineWitdth  線寬
     *  @param filleColor  扇形填充顏色
     *  @param strokeColor 弧線顏色
     */
    -(void)drawArcWithStartAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle lineWidth:(CGFloat)lineWitdth fillColor:(UIColor*)filleColor strokeColor:(UIColor*)strokeColor{
        //保存弧線寬度,開始角度,結束角度
        self.lineWidth=lineWitdth;
        self.startAngle=startAngle;
        self.endAngle=endAngle;
        self.arcAngle=endAngle-startAngle;
        self.arcRadius=Calculate_radius;
        self.scaleRadius=self.arcRadius-self.lineWidth;
        self.scaleValueRadius=self.scaleRadius-self.lineWidth;
        self.speedLabel.text=@"0%";
        
        
        UIBezierPath* outArc=[UIBezierPath bezierPathWithArcCenter:LuCenter radius:self.arcRadius startAngle:startAngle endAngle:endAngle clockwise:YES];
        CAShapeLayer* shapeLayer=[CAShapeLayer layer];
        shapeLayer.lineWidth=lineWitdth;
        shapeLayer.fillColor=filleColor.CGColor;
        shapeLayer.strokeColor=strokeColor.CGColor;
        shapeLayer.path=outArc.CGPath;
        shapeLayer.lineCap=kCALineCapRound;
        [self.layer addSublayer:shapeLayer];
    }
  2. 繪制刻度,可以實現任意等分
    /**
     *  畫刻度
     *
     *  @param divide      刻度幾等分
     *  @param remainder   刻度數
     *  @param strokeColor 輪廓填充顏色
     *  @param fillColor   刻度顏色
     */
    -(void)drawScaleWithDivide:(int)divide andRemainder:(NSInteger)remainder strokeColor:(UIColor*)strokeColor filleColor:(UIColor*)fillColor scaleLineNormalWidth:(CGFloat)scaleLineNormalWidth scaleLineBigWidth:(CGFloat)scaleLineBigWidth{
        
        CGFloat perAngle=self.arcAngle/divide;
        //我們需要計算出每段弧線的起始角度和結束角度
        //這里我們從- M_PI 開始,我們需要理解與明白的是我們畫的弧線與內側弧線是同一個圓心
        for (NSInteger i = 0; i<= divide; i++) {
            
            CGFloat startAngel = (self.startAngle+ perAngle * i);
            CGFloat endAngel   = startAngel + perAngle/5;
            
            UIBezierPath *tickPath = [UIBezierPath bezierPathWithArcCenter:LuCenter radius:self.scaleRadius startAngle:startAngel endAngle:endAngel clockwise:YES];
            CAShapeLayer *perLayer = [CAShapeLayer layer];
            
            if((remainder!=0)&&(i % remainder) == 0) {
                perLayer.strokeColor = strokeColor.CGColor;
                perLayer.lineWidth   = scaleLineBigWidth;
                
            }else{
                perLayer.strokeColor = strokeColor.CGColor;;
                perLayer.lineWidth   = scaleLineNormalWidth;
                
            }
            
            perLayer.path = tickPath.CGPath;
            [self.layer addSublayer:perLayer];
            
        }
    }
  3. 繪制刻度值,刻度值可以按照任意數值等分
    /**
     *  畫刻度值,逆時針設定label的值,將整個儀表切分為N份,每次遞增儀表盤弧度的N分之1
     *
     *  @param divide 刻度值幾等分
     */
    -(void)DrawScaleValueWithDivide:(NSInteger)divide{
        CGFloat textAngel =self.arcAngle/divide;
        if (divide==0) {
            return;
        }
        for (NSUInteger i = 0; i <= divide; i++) {
            CGPoint point = [self calculateTextPositonWithArcCenter:LuCenter Angle:-(self.endAngle-textAngel*i)];
            NSString *tickText = [NSString stringWithFormat:@"%ld%%",(divide - i)*100/divide];
            //默認label的大小23 * 14
            UILabel *text = [[UILabel alloc] initWithFrame:CGRectMake(point.x - 8, point.y - 7, 30, 14)];
            text.text = tickText;
            text.font = [UIFont systemFontOfSize:10.f];
            text.textColor = [UIColor redColor];
            text.textAlignment = NSTextAlignmentLeft;
            [self addSubview:text];
        }
    }
    //默認計算半徑-10,計算label的坐標
    - (CGPoint)calculateTextPositonWithArcCenter:(CGPoint)center
                                           Angle:(CGFloat)angel
    {
        CGFloat x = (self.scaleValueRadius+3*self.lineWidth)* cosf(angel);
        CGFloat y = (self.scaleValueRadius+3*self.lineWidth)* sinf(angel);
        return CGPointMake(center.x + x, center.y - y);
    }
  4. 進度條曲線
    /**
     *  進度條曲線
     *
     *  @param fillColor   填充顏色
     *  @param strokeColor 輪廓顏色
     */
    - (void)drawProgressCicrleWithfillColor:(UIColor*)fillColor strokeColor:(UIColor*)strokeColor{
        UIBezierPath *progressPath  = [UIBezierPath bezierPathWithArcCenter:LuCenter radius:self.arcRadius startAngle:self.startAngle endAngle:self.endAngle clockwise:YES];
        CAShapeLayer *progressLayer = [CAShapeLayer layer];
        self.progressLayer = progressLayer;
        progressLayer.lineWidth = self.lineWidth+0.25f;
        progressLayer.fillColor = fillColor.CGColor;
        progressLayer.strokeColor = strokeColor.CGColor;
        progressLayer.path = progressPath.CGPath;
        progressLayer.strokeStart = 0;
        progressLayer.strokeEnd = 0.0;
        progressLayer.lineCap=kCALineCapRound;
        [self.layer addSublayer:progressLayer];
    }
  5. 為進度條添加漸變圖層,圖層顏色是從左向右漸變
    /**
     *  添加漸變圖層
     *
     *  @param colorGradArray 顏色數組,如果想達到紅-黃-紅效果,數組應該是紅,黃,紅
     */
    -(void)setColorGrad:(NSArray*)colorGradArray{
        //漸變圖層
        CALayer *gradientLayer = [CALayer layer];
        CAGradientLayer *gradientLayer1 =  [CAGradientLayer layer];
        //增加漸變圖層,frame為當前layer的frame
        gradientLayer1.frame = CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height);
        [gradientLayer1 setColors:colorGradArray];
        [gradientLayer1 setStartPoint:CGPointMake(0, 1)];
        [gradientLayer1 setEndPoint:CGPointMake(1, 1)];
        [gradientLayer addSublayer:gradientLayer1];
        
        [gradientLayer setMask:_progressLayer]; //用progressLayer來截取漸變層
        [self.layer addSublayer:gradientLayer];
    }

    個人寫代碼喜歡添加注釋,可是在奈何文筆不行,表達不清,請移步到我的GitHub上下載Demo,如果有什么問題也可以給我發郵件,評論探討。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM