功能:
1. 可改变 背景/中心图标/文字描述 (此进度图显示的刻度为背景图片)
2. 绘制进度时有动画和非动画2种模式,并显示当前value值
效果.gif
1. 创建FYCircleView继承UIView
FYCircleView.h:
@interface FYCircleView : UIView
@property int minNum;
@property int maxNum;
@property NSString *units;
@property(nonatomic,strong) NSString *backImageName;
@property(nonatomic,strong) NSString *iconName;
@property(nonatomic,strong) NSString *meaningStr;
//进度 [0...1]
@property(nonatomic,assign) CGFloat lastProgress;
@property(nonatomic,assign) CGFloat progress;
@property int flag;
@property (nonatomic,copy) void (^progressChange)(NSString *result,int flag);
- (void)updateProgress:(CGFloat)progress animation:(BOOL)animationed color:(UIColor *)color value:(int)value;
@end
FYCircleView.m:
#define DEGREES_TO_RADIANS(degrees) ((M_PI * degrees)/ 180)
#define CATProgressStartAngle (-220) // 进度圆环起始角度
#define CATProgressEndAngle (40) // 进度圆环结束角度
// 3种颜色
#define FYEnvGreenColor UIColorFromRGB(0x00e65c)
#define FYEnvYellowColor UIColorFromRGB(0xffff00)
#define FYEnvRedColor UIColorFromRGB(0xff6462)
@interface JKCircleView () <UIGestureRecognizerDelegate,CAAnimationDelegate>
// 进度圆环
@property CGFloat dialRadius;
// 背景圆环
@property CGFloat outerRadius; // 不要设置除非需要方形页面
@property CGFloat arcRadius; // clipsToBounds时必须<outerRadius
@property CGFloat arcThickness; // 圆环宽度
@property CGPoint trueCenter;
@property UILabel *numberLabel;
@property UILabel *meaningLabel;
@property UIImageView *iconImage;
@property UIImageView *backImage;
@property int currentNum;
@property double angle;
@property (nonatomic, strong) CAShapeLayer *trackLayer;
@property (nonatomic, strong) CAShapeLayer *progressLayer;
@property (nonatomic, strong) NSTimer *timer;
@end
@implementation JKCircleView
- (id) initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if(self){
self.userInteractionEnabled = YES;
self.clipsToBounds = YES;
self.backgroundColor = [UIColor clearColor];
// 设置默认值
self.minNum = 0;
self.maxNum = 100;
self.currentNum = self.minNum;
self.units = @"";
self.iconName = @"温度";
self.backImageName = @"背景";
self.lastProgress = 0.0;
CGFloat width = frame.size.width;
CGFloat height = frame.size.height;
self.trueCenter = CGPointMake(width/2, height/2);
// 设置radius
self.dialRadius = 10;
self.arcRadius = 80;
self.outerRadius = MIN(width, height)/2;
self.arcThickness = 8.0;
// 背景图片
self.backImage = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, width, height-30)];
self.backImage.image = [UIImage imageNamed:self.backImageName];
[self addSubview:self.backImage];
// 中心数值
self.numberLabel = [[UILabel alloc] initWithFrame:CGRectMake(width*.1, height/2 - width/6, 85, 25)];
self.numberLabel.text = [NSString stringWithFormat:@"%d", self.currentNum];
self.numberLabel.center = CGPointMake(self.trueCenter.x, self.trueCenter.y+20);
self.numberLabel.textAlignment = NSTextAlignmentCenter;
self.numberLabel.font = [UIFont systemFontOfSize:16];
self.numberLabel.textColor = [UIColor whiteColor];
[self addSubview:self.numberLabel];
// 中心图标
self.iconImage = [[UIImageView alloc] initWithFrame:CGRectMake(width*.1, height/2 - width/6, 35, 35)];
self.iconImage.center = CGPointMake(self.trueCenter.x, self.trueCenter.y - 10);
self.iconImage.image = [UIImage imageNamed:self.iconName];
[self addSubview:self.iconImage];
// 下方内容
self.meaningLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, height - 20, width, 20)];
self.meaningLabel.text = [NSString stringWithFormat:@"%@",self.meaningStr];
self.meaningLabel.textAlignment = NSTextAlignmentCenter;
self.meaningLabel.font = [UIFont systemFontOfSize:14];
self.meaningLabel.textColor = [UIColor whiteColor];
[self addSubview:self.meaningLabel];
// 背景轨道
_trackLayer=[CAShapeLayer layer];
_trackLayer.frame = self.bounds;
_trackLayer.fillColor = [UIColor clearColor].CGColor;
_trackLayer.strokeColor = [UIColor whiteColor].CGColor;
_trackLayer.opacity = 0.25; //背景圆环的背景透明度
_trackLayer.lineCap = kCALineCapRound;
[self.layer addSublayer:_trackLayer];
self.arcRadius = MIN(self.arcRadius, self.outerRadius - self.dialRadius);
UIBezierPath *path=[UIBezierPath bezierPathWithArcCenter:CGPointMake(self.frame.size.width/2, self.frame.size.height/2) radius:self.arcRadius startAngle:DEGREES_TO_RADIANS(CATProgressStartAngle) endAngle:DEGREES_TO_RADIANS(CATProgressEndAngle) clockwise:YES];//-210到30的path
_trackLayer.path = path.CGPath;
_trackLayer.lineWidth = self.arcThickness;
// 进度轨道
_progressLayer = [CAShapeLayer layer];
_progressLayer.frame = self.bounds;
_progressLayer.fillColor = [[UIColor clearColor] CGColor];
_progressLayer.strokeColor = FYEnvGreenColor.CGColor; //!不能用clearColor
_progressLayer.lineCap=kCALineCapRound;
_progressLayer.strokeEnd = 0.0;
[self.layer addSublayer:_progressLayer];
self.arcRadius = MIN(self.arcRadius, self.outerRadius - self.dialRadius);
CGFloat start = CATProgressStartAngle;
CGFloat end = CATProgressEndAngle;
UIBezierPath *path1=[UIBezierPath bezierPathWithArcCenter:CGPointMake(self.frame.size.width/2, self.frame.size.height/2) radius:self.arcRadius startAngle:DEGREES_TO_RADIANS(start) endAngle:DEGREES_TO_RADIANS(end) clockwise:YES];//-210到30的path
_progressLayer.path = path1.CGPath;
_progressLayer.lineWidth = self.arcThickness;
}
return self;
}
- (void)setProgress:(CGFloat)progress {
_progress = progress;
if (progress < 0.0) _progress = 0.0;
if (progress > 1.0) _progress = 1.0;
}
- (void)updateProgress:(CGFloat)progress animation:(BOOL)animationed color:(UIColor *)color value:(int)value{
self.progress = progress;
[self.progressLayer removeAllAnimations];
// 非动画形式
if (!animationed) {
[CATransaction begin];
[CATransaction setDisableActions:YES];
[CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]];
[CATransaction setAnimationDuration:0.5];
self.progressLayer.strokeEnd = self.progress;
self.currentNum = self.minNum + (self.maxNum - self.minNum)*progress;
self.numberLabel.text = [NSString stringWithFormat:@"%d", self.currentNum];
if (self.progressChange) {
self.progressChange([NSString stringWithFormat:@"%d",self.currentNum],self.flag);
}
[CATransaction commit];
//动画形式
} else {
CABasicAnimation *animation= [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
animation.fromValue = @(self.lastProgress);
animation.toValue = @(progress);
animation.duration = 1.5 * self.progress;
animation.removedOnCompletion = YES;
animation.delegate = self;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
_progressLayer.strokeColor = color.CGColor;
self.progressLayer.strokeEnd = self.progress;
[self.progressLayer addAnimation:animation forKey:@"strokeEndAnimation"];
self.currentNum = value;
self.numberLabel.text = [NSString stringWithFormat:@"%d", self.currentNum];
if (self.progressChange) {
self.progressChange([NSString stringWithFormat:@"%d",self.currentNum],self.flag);
}
}
self.lastProgress = progress;
}
- (void) willMoveToSuperview:(UIView *)newSuperview{
[super willMoveToSuperview:newSuperview];
self.arcRadius = MIN(self.arcRadius, self.outerRadius - self.dialRadius);
self.numberLabel.text = [NSString stringWithFormat:@"%d", self.currentNum];
self.meaningLabel.text = [NSString stringWithFormat:@"%@",self.meaningStr];
self.backImage.image = [UIImage imageNamed:self.backImageName];
self.iconImage.image = [UIImage imageNamed:self.iconName];
}
2. 创建view
//背景图片
NSArray *backImageArr = @[@"temperature_back",
@"wet_back",
@"posionGas_back",
@"carbonDioxide_back",
@"PM2.5_back",
@"noise_back"];
//图标
NSArray *imageArr = @[@"temperature_icon",
@"wet_icon",
@"posionGas_icon",
@"carbonDioxide_icon",
@"PM2.5_icon",
@"noise_icon"];
//value单位,此处并没有应用
NSArray *tipArr = @[@"℃",@"m³/h",@"%",@"%",@"%",@"%"];
//下方描述
NSArray *meanLabelArr = @[@"温度(℃)", @"湿度(%RH)", @"有害气体(PPM)", @"二氧化碳(%)", @"PM2.5(MG/M³)", @"噪音(DB)"];
CGFloat W = FYProgressLoop_W;
CGFloat H = FYProgressLoop_H;
CGFloat marginX = (SCREEN_WIDTH - W*2 - FYViewMagin*2)/2;
for (int i = 0; i < 6; i++) {
int col_idx = i % 2; //列索引
int row_idx = i / 2; //行索引
CGFloat X = FYViewMagin + col_idx * (W + 2*marginX);
CGFloat Y = 60 + row_idx * (H + 20);
JKCircleView *dialView = [[JKCircleView alloc] initWithFrame:CGRectMake(X, Y, W, H)];
//取值范围:最大值、最小值
dialView.minNum = 0;
if (i == 0) dialView.maxNum = 35;
if (i == 1) dialView.maxNum = 100;
if (i == 2) dialView.maxNum = 180;
if (i == 3) dialView.maxNum = 1500;
if (i == 4) dialView.maxNum = 100;
if (i == 5) dialView.maxNum = 150;
dialView.flag = i+100;
dialView.tag = i+100;
dialView.units = tipArr[i]; //单位名称
dialView.backImageName = backImageArr[i]; //背景图片
dialView.iconName = imageArr[i]; //中间图标
dialView.progress = 0.0;
dialView.meaningStr = meanLabelArr[i];
[self.view addSubview:dialView];
}
3. 赋值
//测试数据
wendu1 = [self getRandomNumberFrom:15 to:35];
wet2 = 36;
posionGas3 = [self getRandomNumberFrom:20 to:130];
eyht4 = [self getRandomNumberFrom:200 to:1200];
PM5 = [self getRandomNumberFrom:0 to:80];
noise6 = [self getRandomNumberFrom:0 to:90];
// 注意:此f值为测试出来的误差值所做的修复,当进度超过50%不会有误差,小于50%时会有。
CGFloat f = 0.03;
for (int i = 0; i < 6; i++) {
JKCircleView *dialView = [self.view viewWithTag:100+i];
switch (i) {
case 0:
[dialView updateProgress:(wendu1*1.0/35>0.5||wendu1*1.0/35<f ? wendu1*1.0/35 : wendu1*1.0/35-f)animation:YES color:_wenduColor value:wendu1];
break;
case 1:
[dialView updateProgress:(wet2*1.0/100>0.5||wet2*1.0/100<f ? wet2*1.0/100 : wet2*1.0/100-f) animation:YES color:_wetColor value:wet2];
break;
case 2:
[dialView updateProgress:(posionGas3*1.0/180>0.5||posionGas3*1.0/180<f ? posionGas3*1.0/180 : posionGas3*1.0/180-f) animation:YES color:_posionGasColor value:posionGas3];
break;
case 3:
[dialView updateProgress:(eyht4*1.0/1500>0.5||eyht4*1.0/1500<f ? eyht4*1.0/1500 : eyht4*1.0/1500-f) animation:YES color:_eyhtColor value:eyht4];
break;
case 4:
[dialView updateProgress:(PM5*1.0/100>0.5||PM5*1.0/100>0.5<f ? PM5*1.0/100 : PM5*1.0/100-f) animation:YES color:_PMColor value:PM5];
break;
case 5:
[dialView updateProgress:(noise6*1.0/150>0.5||noise6*1.0/150<f ? noise6*1.0/150 : noise6*1.0/150-f) animation:YES color:_noiseColor value:noise6];
break;
default:
break;
}
}