IOS 自定义UIView--简易的环形进度条,可随手指更新进度,类似华为的天气进度

1.放上效果图先:
这里写图片描述

本效果难点:

  • 扩展UIView 重载drawRect接口,了解uiView绘图机制,刷新机制
  • 弧度与角度的互转
  • 任意2点之间计算角度

下面我们一步一步来,先将黄色的刻度画出来,使用CoreGraphices画图,计算每个黄线的度标使用三角函数,这里将x = r* cos(angle) y = r * sin(angle);反过来是一样,只是画的方向不一致布局,这个不是重点。本案例中,黄色的刻度为60个,计算每个黄线的起始点与终点座标方法为:

- (void)drawRect:(CGRect)rect {

    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetLineWidth(context, 0.5);

    for (float angle = 0; angle <= 360; angle += 6.0) {
        float xStart = [self calculateX:self.r angle:angle];//calculateX(r, angle);
        float xStop = [self calculateX:self.r - self.l angle:angle];//calculateX(r - l, angle);

        float yStart = [self calculateY:self.r angle:angle];//calculateY(r, angle);
        float yStop = [self calculateY:self.r - self.l angle:angle];//calculateY(r - l, angle);

        if (angle <= self.stopAngle && angle >= self.startAngle) {
           // linePaint.setShader(shader);
            CGContextSetStrokeColorWithColor(context, [[UIColor blueColor] CGColor]);//线框颜色

        } else {
            //linePaint.setShader(shaderWhite);
            CGContextSetStrokeColorWithColor(context, [[UIColor yellowColor] CGColor]);//线框颜色

        }

        if (angle == self.stopAngle) {

            float xStartL = [self calculateX:self.r * 1.05f angle:angle];//calculateX(r * 1.05f, angle);
            float xStopL = [self calculateX:self.r - self.l angle:angle];//calculateX((r - l), angle);

            float yStartL = [self calculateY:self.r * 1.05f angle:angle];//calculateY(r * 1.05f, angle);
            float yStopL = [self calculateY:self.r  - self.l angle:angle];//calculateY((r - l), angle);
            //canvas.drawLine(xStartL, yStartL, xStopL, yStopL, linePaint);    //画彩色的长线

            CGContextMoveToPoint(context, xStartL, yStartL);
            CGContextAddLineToPoint(context, xStopL,yStopL);

        } else {
            //canvas.drawLine(xStart, yStart, xStop, yStop, linePaint);           //画所有的短线

            CGContextMoveToPoint(context, xStart, yStart);
            CGContextAddLineToPoint(context, xStop,yStop);
        }


        CGContextStrokePath(context);   // 关闭路径渲染
    }

}

下面再给出几个核心方法:因为有注释,这里不再说明 :

/**
 根据半径和角度计算x坐标

 @param r 半径
 @param angle 角度
 @return x 座标
 */
-(float) calculateX:(float) r angle:(float) angle{

    angle = angle * ((2 * PI) / 360);  //转换为弧度
    double x = r * sin(angle);
    double xFinal = self.centerX + x;
    return (float) xFinal;

}

-(float) calculateY:(float)r angle:(float) angle{

    angle = angle * ((2 * PI) / 360);
    double y = r * cos(angle);
    double yFinal = self.centerY - y;
    return (float) yFinal;
}


/**
 弧度转角度

 @param radian 弧度
 @return 角度值
 */
-(float) radian2Angle:(float)radian{

    float tmp = round(radian / PI * 180);
    return tmp >= 0 ? tmp : 360 + tmp;
}


/**
 2点之间计算角度

 @param x1
 @param y1
 @param x2
 @param y2
 @return
 */
-(float) getAngle:(float)x1 posy1:(float)y1 posx2:(float)x2 posy2:(float) y2{

    // 两点在X轴的距离
    float lenX = (float) (x2 - x1);
    // 两点在Y轴距离
    float lenY = (float) (y2 - y1);
    // 两点距离
    float lenXY = (float) sqrt((double) (lenX * lenX + lenY * lenY));
    // 计算弧度
    float radian = (float) acos(lenX / lenXY) * (y2 < y1 ? -1 : 1);
    // 计算角度
    double angle = [self radian2Angle:radian];//radian2Angle(radian);

    return (float) angle;
}

-(float) comfirmNumIs6Times:(int)n{
    float mol = n % 6;
    if (mol == 0) {
        return n;
    }
    return n - mol;
}
-(Boolean)updateStopAngle:(float) stopAngle{

    if (stopAngle >= self.startAngle && stopAngle <= 360) {
        self.stopAngle = stopAngle;
        [self setNeedsDisplay];//invalidate();
        return true;
    } else
        return false;
}

下面处理手指滑动刻度随着变化的逻辑—重载UIView的touch事件即可:

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

    NSSet *allTouches = [event allTouches];    //返回与当前接收者有关的所有的触摸对象
    UITouch *touch = [allTouches anyObject];   //视图中的所有对象
    CGPoint point = [touch locationInView:[touch view]]; //返回触摸点在视图中的当前坐标
    self.actionDownX = point.x;
    self.actionDownY = point.y;
    //NSLog(@"170-------:%f",point.x);
}

- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

    NSSet *allTouches = [event allTouches];    //返回与当前接收者有关的所有的触摸对象
    UITouch *touch = [allTouches anyObject];   //视图中的所有对象
    CGPoint point = [touch locationInView:[touch view]];

    float newAngle = [self getAngle:self.actionDownX posy1:self.actionDownY posx2:point.x posy2:point.y];//getAngle(actionDownX, actionDownY, event.getX(), event.getY());

    newAngle = [self comfirmNumIs6Times:newAngle];//comfirmNumIs6Times(newAngle);    //如果不取刻度值,滑动起来就会变得很流畅

    //stopTem = (int) (newAngle / 6);
    //setStopAngle(newAngle);
    [self updateStopAngle:newAngle];
    //NSLog(@"186---------:move:%f",point.x);
}

- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{


}

整个文件的代码为:

//
//  HWCircleProgressView.m
//  BZCustomView
//
//  Created by 刘小兵 on 2018/6/7.
//  Copyright © 2018 刘小兵. All rights reserved.
//

#import "HWCircleProgressView.h"
#define PI 3.1415926

@implementation HWCircleProgressView

-(instancetype) initWithFrame:(CGRect)frame{
    if(self =[super initWithFrame:frame]){

        self.stopAngle = 90.0f;
        self.startAngle = 0.0f;
    }
    return self;
}

-(void) layoutSubviews{

    self.centerX = self.frame.size.width / 2;
    self.centerY = self.frame.size.height / 2;

    self.r = self.frame.size.width / 2 * 0.8f;
    self.l = self.frame.size.width / 2 * 0.1f;

}


/**
 根据半径和角度计算x坐标

 @param r 半径
 @param angle 角度
 @return x 座标
 */
-(float) calculateX:(float) r angle:(float) angle{

    angle = angle * ((2 * PI) / 360);  //转换为弧度
    double x = r * sin(angle);
    double xFinal = self.centerX + x;
    return (float) xFinal;

}

-(float) calculateY:(float)r angle:(float) angle{

    angle = angle * ((2 * PI) / 360);
    double y = r * cos(angle);
    double yFinal = self.centerY - y;
    return (float) yFinal;
}


/**
 弧度转角度

 @param radian 弧度
 @return 角度值
 */
-(float) radian2Angle:(float)radian{

    float tmp = round(radian / PI * 180);
    return tmp >= 0 ? tmp : 360 + tmp;
}


/**
 2点之间计算角度

 @param x1
 @param y1
 @param x2
 @param y2
 @return
 */
-(float) getAngle:(float)x1 posy1:(float)y1 posx2:(float)x2 posy2:(float) y2{

    // 两点在X轴的距离
    float lenX = (float) (x2 - x1);
    // 两点在Y轴距离
    float lenY = (float) (y2 - y1);
    // 两点距离
    float lenXY = (float) sqrt((double) (lenX * lenX + lenY * lenY));
    // 计算弧度
    float radian = (float) acos(lenX / lenXY) * (y2 < y1 ? -1 : 1);
    // 计算角度
    double angle = [self radian2Angle:radian];//radian2Angle(radian);

    return (float) angle;
}

-(float) comfirmNumIs6Times:(int)n{
    float mol = n % 6;
    if (mol == 0) {
        return n;
    }
    return n - mol;
}
-(Boolean)updateStopAngle:(float) stopAngle{

    if (stopAngle >= self.startAngle && stopAngle <= 360) {
        self.stopAngle = stopAngle;
        [self setNeedsDisplay];//invalidate();
        return true;
    } else
        return false;
}


- (void)drawRect:(CGRect)rect {

    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetLineWidth(context, 0.5);

    for (float angle = 0; angle <= 360; angle += 6.0) {
        float xStart = [self calculateX:self.r angle:angle];//calculateX(r, angle);
        float xStop = [self calculateX:self.r - self.l angle:angle];//calculateX(r - l, angle);

        float yStart = [self calculateY:self.r angle:angle];//calculateY(r, angle);
        float yStop = [self calculateY:self.r - self.l angle:angle];//calculateY(r - l, angle);

        if (angle <= self.stopAngle && angle >= self.startAngle) {
           // linePaint.setShader(shader);
            CGContextSetStrokeColorWithColor(context, [[UIColor blueColor] CGColor]);//线框颜色

        } else {
            //linePaint.setShader(shaderWhite);
            CGContextSetStrokeColorWithColor(context, [[UIColor yellowColor] CGColor]);//线框颜色

        }

        if (angle == self.stopAngle) {

            float xStartL = [self calculateX:self.r * 1.05f angle:angle];//calculateX(r * 1.05f, angle);
            float xStopL = [self calculateX:self.r - self.l angle:angle];//calculateX((r - l), angle);

            float yStartL = [self calculateY:self.r * 1.05f angle:angle];//calculateY(r * 1.05f, angle);
            float yStopL = [self calculateY:self.r  - self.l angle:angle];//calculateY((r - l), angle);
            //canvas.drawLine(xStartL, yStartL, xStopL, yStopL, linePaint);    //画彩色的长线

            CGContextMoveToPoint(context, xStartL, yStartL);
            CGContextAddLineToPoint(context, xStopL,yStopL);

        } else {
            //canvas.drawLine(xStart, yStart, xStop, yStop, linePaint);           //画所有的短线

            CGContextMoveToPoint(context, xStart, yStart);
            CGContextAddLineToPoint(context, xStop,yStop);
        }


        CGContextStrokePath(context);   // 关闭路径渲染
    }

}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

    NSSet *allTouches = [event allTouches];    //返回与当前接收者有关的所有的触摸对象
    UITouch *touch = [allTouches anyObject];   //视图中的所有对象
    CGPoint point = [touch locationInView:[touch view]]; //返回触摸点在视图中的当前坐标
    self.actionDownX = point.x;
    self.actionDownY = point.y;
    //NSLog(@"170-------:%f",point.x);
}

- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

    NSSet *allTouches = [event allTouches];    //返回与当前接收者有关的所有的触摸对象
    UITouch *touch = [allTouches anyObject];   //视图中的所有对象
    CGPoint point = [touch locationInView:[touch view]];

    float newAngle = [self getAngle:self.actionDownX posy1:self.actionDownY posx2:point.x posy2:point.y];//getAngle(actionDownX, actionDownY, event.getX(), event.getY());

    newAngle = [self comfirmNumIs6Times:newAngle];//comfirmNumIs6Times(newAngle);    //如果不取刻度值,滑动起来就会变得很流畅

    //stopTem = (int) (newAngle / 6);
    //setStopAngle(newAngle);
    [self updateStopAngle:newAngle];
    //NSLog(@"186---------:move:%f",point.x);
}

- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{


}


@end

.h文件为:

//
//  HWCircleProgressView.h
//  BZCustomView
//
//  Created by 刘小兵 on 2018/6/7.
//  Copyright © 2018 刘小兵. All rights reserved.
//  仿华为天气圆形进度条

#import <UIKit/UIKit.h>

@interface HWCircleProgressView : UIView

@property(nonatomic,assign) NSInteger count;
@property(nonatomic,assign) float centerX,centerY;
@property(nonatomic,assign) float r,l;
@property(nonatomic,assign) float startAngle;
@property(nonatomic,assign) float stopAngle;
@property(nonatomic,assign) CGFloat actionDownX,actionDownY;


-(Boolean)updateStopAngle:(float) stopAngle;

@end

猜你喜欢

转载自blog.csdn.net/d06110902002/article/details/80606938