因为业务需求,自定义的一个表盘控件
1:刻度值和指针支持颜色渐变
2:因为都是自己写的,自己在用,就没有将很多修改的方式抽出方法来,如果想用的兄弟可以自己好好看看源码,实现原理其实也不难,当学习吧
android版本:https://blog.csdn.net/qq_15327175/article/details/134155127
看效果图,动画和数值变化我这儿就不展示了,有类似需求的朋友可以自己研究研究
WBBDashBoard.h
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface WBBDashBoard : UIView
@property (nonatomic, assign) float value;
@end
NS_ASSUME_NONNULL_END
WBBDashBoard.m
#import "WBBDashBoard.h"
#define toRad(angle) ((angle) * M_PI / 180)
@interface WBBDashBoard ()
/**
* 指针中心
*/
@property (nonatomic, assign) CGPoint dotCenter;
/**
* 仪表半径
*/
@property (nonatomic, assign) CGFloat radius;
/**
* 圆盘开始角度
*/
@property(nonatomic,assign)CGFloat startAngle;
/**
* 圆盘结束角度
*/
@property(nonatomic,assign)CGFloat endAngle;
/**
* 圆盘总共弧度弧度
*/
@property(nonatomic,assign)CGFloat arcAngle;
/**
* 指针
*/
@property (nonatomic, strong) CAShapeLayer *pointLayer;
/**
* 指针固定点
*/
@property (nonatomic, strong) CAShapeLayer *bPointLayer;
@property (nonatomic, strong) UILabel *valueLable;
/**
* 表盘值宽
*/
@property(nonatomic,assign) CGFloat valueW;
/**
* 表盘值高
*/
@property(nonatomic,assign)CGFloat valueH;
/**
* 最大值
*/
@property(nonatomic,assign)CGFloat maxValue;
/**
* 最小值
*/
@property(nonatomic,assign)CGFloat minValue;
/**
* 渐变圆环
*/
@property (nonatomic, strong)CAShapeLayer* arcLayer;
@end
@implementation WBBDashBoard
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
}
*/
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
_value=1.0f;
self.dotCenter = CGPointMake(frame.size.width/2.0, frame.size.width/2.0);
self.radius = frame.size.width/2.0;
self.startAngle=toRad(-225);
self.endAngle=toRad(45);
self.valueW=24.f;
self.valueH=16.f;
self.maxValue=3.0f;
self.minValue=1.0f;
self.arcAngle= fabsf(self.startAngle - self.endAngle);
[self loadSubViews];
}
return self;
}
- (void)loadSubViews {
[self drawArcWithStartAngle];
[self DrawScaleValue];
[self drawPoint];
[self setTitle:@"PUE"];
}
/**
* 画弧度
*
* @param startAngle 开始角度
* @param endAngle 结束角度
* @param lineWitdth 线宽
* @param filleColor 扇形填充颜色
* @param strokeColor 弧线颜色
*/
-(void)drawArcWithStartAngle{
//内环Mask
UIBezierPath *outArc=[UIBezierPath bezierPathWithArcCenter:_dotCenter radius:_radius startAngle:_startAngle endAngle:_endAngle clockwise:YES];
CAShapeLayer* shapeLayer1=[CAShapeLayer layer];
shapeLayer1.lineWidth=3;
shapeLayer1.fillColor=[UIColor clearColor].CGColor;
shapeLayer1.path=outArc.CGPath;
shapeLayer1.strokeColor=[UIColor whiteColor].CGColor;
[self.layer addSublayer:shapeLayer1];
double ArcLenght=0;
// //Mask
outArc=[UIBezierPath bezierPathWithArcCenter:_dotCenter radius:_radius-4 startAngle:_startAngle endAngle:_endAngle clockwise:YES];
CAShapeLayer* shapeLayer2=[CAShapeLayer layer];
shapeLayer2.lineWidth=4;
shapeLayer2.fillColor=[UIColor clearColor].CGColor;
shapeLayer2.path=outArc.CGPath;
ArcLenght=((2*M_PI*(_radius-4)*3/4)-21*1)/20;
[shapeLayer2 setLineDashPattern:[NSArray arrayWithObjects:[NSNumber numberWithDouble:1], [NSNumber numberWithDouble:ArcLenght], nil]];//0:长度 1:空隙
shapeLayer2.strokeColor=[UIColor whiteColor].CGColor;
[self.layer addSublayer:shapeLayer2];
outArc=[UIBezierPath bezierPathWithArcCenter:_dotCenter radius:_radius-4 startAngle:_startAngle endAngle:_endAngle clockwise:YES];
CAShapeLayer* shapeLayer3=[CAShapeLayer layer];
shapeLayer3.lineWidth=6;
shapeLayer3.fillColor=[UIColor clearColor].CGColor;
shapeLayer3.path=outArc.CGPath;
ArcLenght=((2*M_PI*(_radius-4)*3/4)-5*2)/4;
[shapeLayer3 setLineDashPattern:[NSArray arrayWithObjects:[NSNumber numberWithDouble:2], [NSNumber numberWithDouble:ArcLenght], nil]];//0:长度 1:空隙
shapeLayer3.strokeColor=[UIColor whiteColor].CGColor;
[self.layer addSublayer:shapeLayer3];
}
/**
* 画刻度值,逆时针设定label的值,将整个仪表切分为N份,每次递增仪表盘弧度的N分之1
*
* @param divide 刻度值几等分
*/
-(void)DrawScaleValue{
CGFloat textAngel =self.arcAngle/4;
for (NSUInteger i = 0; i <= 4; i++) {
CGPoint point = [self calculateTextPositonWithArcCenter:_dotCenter Angle:(self.startAngle+textAngel*i)];
NSString *tickText;
if (i == 0) {
tickText =@"1.0";
} else if (i == 4) {
tickText =@"3.0";
} else {
tickText = [NSString stringWithFormat:@"%.1f",(3.0f-1.0f)/4.0f*i+1.0f];
}
//默认label的大小23 * 14
UILabel *text = [[UILabel alloc] initWithFrame:CGRectMake(point.x, point.y, _valueW, _valueH)];
text.text = tickText;
text.font = [UIFont systemFontOfSize:12.f];
text.textColor = [UIColor whiteColor];
// text.backgroundColor=[UIColor greenColor];
text.textAlignment = NSTextAlignmentCenter;
[self addSubview:text];
}
}
//默认计算半径-10,计算label的坐标
- (CGPoint)calculateTextPositonWithArcCenter:(CGPoint)center
Angle:(CGFloat)angel
{
CGFloat x = center.x +(_radius)* cosf(angel);
CGFloat y = center.y +(_radius)* sinf(angel);
// y=y-_valueH/2;
if(angel>=toRad(5)&&angel<=toRad(85)||angel>=toRad(-275)&&angel<=toRad(-355)){
y=y-_valueH/2;
x=x+_valueW/3;
}else if(angel>toRad(85)&&angel<toRad(95)||angel>toRad(-265)&&angel<toRad(-275)){
y=y-_valueH/2+_valueW/3;
x=x-x+_valueW/2;
}
else if(angel>=toRad(95)&&angel<=toRad(175)||angel>=toRad(-265)&&angel<=toRad(-185)){
y=y-_valueH/2;
x=x-_valueW-_valueW/3;
}else if(angel>toRad(175)&&angel<toRad(185)||angel>toRad(-185)&&angel<toRad(-175)){
y=y-_valueH/2;
x=x-_valueW-_valueW/3;
}
else if(angel>=toRad(185)&&angel<=toRad(265)||angel>=toRad(-175)&&angel<=toRad(-95)){
y=y-_valueH/2;
x=x-_valueW-_valueW/3;
}else if(angel>toRad(265)&&angel<toRad(275)||angel>toRad(-95)&&angel<toRad(-85)){
y=y-_valueH;
x=x-_valueW/2;
}
else if(angel>=toRad(275)&&angel<=toRad(355)||angel>=toRad(-85)&&angel<=toRad(-5)){
y=y-_valueH/2;
x=x+_valueW/3;
}else if(angel>toRad(355)&&angel<toRad(5)||angel>toRad(-5)&&angel<toRad(5)){
y=y-_valueH/2;
x=x+_valueW/3;
}
// NSLog([NSString stringWithFormat:@"0:%f",toRad(0)]);
// NSLog(@"--------------------------------------------------------------------------------");
// NSLog([NSString stringWithFormat:@"90:%f",toRad(90)]);
// NSLog([NSString stringWithFormat:@"180:%f",toRad(180)]);
// NSLog([NSString stringWithFormat:@"270:%f",toRad(270)]);
// NSLog(@"--------------------------------------------------------------------------------");
// NSLog([NSString stringWithFormat:@"-90:%f",toRad(-90)]);
// NSLog([NSString stringWithFormat:@"-180:%f",toRad(-180)]);
// NSLog([NSString stringWithFormat:@"-270:%f",toRad(-270)]);
return CGPointMake(x, y);
}
-(void)setTitle:(NSString *)title {
UILabel *text = [[UILabel alloc] initWithFrame:CGRectMake(self.dotCenter.x-25, self.dotCenter.y+20, 50, 20)];
text.text = title;
text.font = [UIFont systemFontOfSize:12.f];
text.textColor = [UIColor whiteColor];
// text.backgroundColor=[UIColor greenColor];
text.textAlignment = NSTextAlignmentCenter;
[self addSubview:text];
_valueLable=[[UILabel alloc] initWithFrame:CGRectMake(self.dotCenter.x-25, self.dotCenter.y+40, 50, 20)];
_valueLable.text=@"--";
_valueLable.textAlignment=NSTextAlignmentCenter;
_valueLable.font=[UIFont systemFontOfSize:14];
_valueLable.textColor=[UIColor whiteColor];
[self addSubview:_valueLable];
}
- (void)drawPoint {
UIBezierPath *outArc=[UIBezierPath bezierPathWithArcCenter:_dotCenter radius:_radius-3 startAngle:_startAngle endAngle:_startAngle clockwise:YES];
_arcLayer=[CAShapeLayer layer];
_arcLayer.lineWidth=6;
_arcLayer.fillColor=[UIColor clearColor].CGColor;
_arcLayer.path=outArc.CGPath;
_arcLayer.strokeColor=[UIColor whiteColor].CGColor;
NSArray *colors1= [NSArray arrayWithObjects:
(id)[[UIColor kh_colorWithHexString:@"36e6fd" andAlpha:0.2] CGColor],
(id)[[UIColor kh_colorWithHexString:@"60df5a" andAlpha:0.6] CGColor],
(id)[[UIColor kh_colorWithHexString:@"efdf1b" andAlpha:0.7] CGColor],nil];
NSArray *colors2= [NSArray arrayWithObjects:
(id)[[UIColor kh_colorWithHexString:@"efdf1b" andAlpha:0.7] CGColor],
(id)[[UIColor kh_colorWithHexString:@"efdf1b" andAlpha:0.8] CGColor],
(id)[[UIColor kh_colorWithHexString:@"f54f0A" andAlpha:0.8] CGColor],nil];
//渐变图层
CALayer *gradientLayer = [CALayer layer];
CAGradientLayer *gradientLayer1 = [CAGradientLayer layer];
gradientLayer1.frame = CGRectMake(-3, -3, (self.frame.size.width+6)/2, self.frame.size.width+6);
gradientLayer1.shadowPath=outArc.CGPath;
[gradientLayer1 setColors:colors1];
[gradientLayer1 setStartPoint:CGPointMake(0.3, 0.8)];
[gradientLayer1 setEndPoint:CGPointMake(1.0, 0.0)];
[gradientLayer addSublayer:gradientLayer1];
CAGradientLayer *gradientLayer2 = [CAGradientLayer layer];
gradientLayer2.frame = CGRectMake(-3+(self.frame.size.width+6)/2, -3, (self.frame.size.width+6)/2, self.frame.size.width+6);
gradientLayer2.shadowPath=outArc.CGPath;
[gradientLayer2 setColors:colors2];
[gradientLayer2 setStartPoint:CGPointMake(0.0, 0.0)];
[gradientLayer2 setEndPoint:CGPointMake(0.8, 0.7)];
[gradientLayer addSublayer:gradientLayer2];
[gradientLayer setMask:_arcLayer]; //用shapeLayer来截取渐变层
[self.layer addSublayer:gradientLayer];
UIBezierPath* aPath = [UIBezierPath bezierPath];
aPath.lineWidth = 15.0;
aPath.lineCapStyle = kCGLineCapRound ; //终点(起点)样式
aPath.lineJoinStyle = kCGLineCapRound ; //拐点样式
// [aPath moveToPoint:CGPointMake(0, 0)];//设置起始点
// [aPath addLineToPoint:CGPointMake(8, _radius-20)];//途经点
// [aPath addLineToPoint:CGPointMake(0, _radius-20+15)];//途经点
// [aPath addLineToPoint:CGPointMake(-8, _radius-20)];//途经点
[aPath moveToPoint:CGPointMake(0, 0)];//设置起始点>>>>>>>需要旋转的点作为起始点
[aPath addLineToPoint:CGPointMake(0, 5)];//途经点
[aPath addLineToPoint:CGPointMake(-5, 0)];//途经点
[aPath addLineToPoint:CGPointMake(0, -(_radius-15))];//途经点
[aPath addLineToPoint:CGPointMake(5, 0)];//途经点
[aPath addLineToPoint:CGPointMake(0, 5)];//途经点
[aPath moveToPoint:CGPointMake(0, -(_radius-2))];
[aPath addArcWithCenter:CGPointMake(0, -(_radius-2)) radius:4 startAngle:0 endAngle:toRad(360) clockwise:YES];
[aPath closePath];//通过调用closePath方法得到最后一条线
self.pointLayer=[CAShapeLayer layer];
self.pointLayer.lineWidth=3;
self.pointLayer.fillColor=[UIColor kh_colorWithHexString:@"37E5FE"].CGColor;
self.pointLayer.path=aPath.CGPath;
self.pointLayer.strokeColor=[UIColor clearColor].CGColor;
self.pointLayer.position = CGPointMake(self.dotCenter.x, self.dotCenter.y);//将aPath起始点移到中心点,才可以正常旋转()
// shapeLayer1.anchorPoint = CGPointMake(1.0, 1.0);//无效
UIBezierPath *dPath = [UIBezierPath bezierPath];
dPath.lineWidth = 5.0;
dPath.lineCapStyle = kCGLineCapRound; //线条拐角
dPath.lineJoinStyle = kCGLineCapRound; //终点处理
[dPath moveToPoint:CGPointMake(0, -(_radius-2))];
[dPath addArcWithCenter:CGPointMake(0, -(_radius-2)) radius:2 startAngle:0 endAngle:toRad(360) clockwise:YES];
CAShapeLayer *dPointLayer=[CAShapeLayer layer];
dPointLayer.lineWidth=3;
dPointLayer.fillColor=[UIColor whiteColor].CGColor;
dPointLayer.path=dPath.CGPath;
dPointLayer.strokeColor=[UIColor clearColor].CGColor;
[self.pointLayer addSublayer: dPointLayer];
[self.layer addSublayer: self.pointLayer];
CGFloat angle1 = toRad(-135);
self.pointLayer.transform = CATransform3DMakeRotation(angle1, 0, 0, 1);
UIBezierPath *bPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(_dotCenter.x-5, _dotCenter.y-5, 10, 10)];
bPath.lineWidth = 5.0;
bPath.lineCapStyle = kCGLineCapRound; //线条拐角
bPath.lineJoinStyle = kCGLineCapRound; //终点处理
self.bPointLayer=[CAShapeLayer layer];
self.bPointLayer.lineWidth=3;
self.bPointLayer.fillColor=[UIColor kh_colorWithHexString:@"37E5FE"].CGColor;
self.bPointLayer.path=bPath.CGPath;
self.bPointLayer.strokeColor=[UIColor clearColor].CGColor;
[self.layer addSublayer: self.bPointLayer];
UIBezierPath *cPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(_dotCenter.x-2.5, _dotCenter.y-2.5, 5, 5)];
cPath.lineWidth = 5.0;
cPath.lineCapStyle = kCGLineCapRound; //线条拐角
cPath.lineJoinStyle = kCGLineCapRound; //终点处理
CAShapeLayer *cPointLayer=[CAShapeLayer layer];
cPointLayer.lineWidth=3;
cPointLayer.fillColor=[UIColor whiteColor].CGColor;
cPointLayer.path=cPath.CGPath;
cPointLayer.strokeColor=[UIColor clearColor].CGColor;
[self.layer addSublayer: cPointLayer];
//
// CGFloat angle2 = (1.8 - 1.6)/1.2*M_PI;
// self.pointLayer.transform = CATransform3DMakeRotation(angle2, 0, 0, 1);
//
// CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
// animation.duration = 1.0f;
// animation.fromValue = @(angle1);
// animation.toValue = @(angle2);
// animation.fillMode = kCAFillModeForwards;
// animation.removedOnCompletion = NO;
// animation.autoreverses = NO;
// animation.timingFunction=[CAMediaTimingFunction functionWithControlPoints:0.20 :1.50 :0.20 :1.00];
// animation.repeatCount = 1;
//
// // CABasicAnimation *animation2 = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
// // animation2.duration = 2.f;
// animation2.fromValue = @(angle2+0.1*M_PI);
// // animation2.toValue = @(angle2-0.1*M_PI);
// // animation2.fillMode = kCAFillModeForwards;
// // animation2.removedOnCompletion = NO;
// // animation2.repeatCount = 1;
//
// // CABasicAnimation *animation3 = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
// // animation3.duration = 2.f;
// // animation3.fromValue = @(angle2-0.2*M_PI);
// // animation3.toValue = @(angle2);
// // animation3.fillMode = kCAFillModeForwards;
// // animation3.removedOnCompletion = NO;
// // animation3.repeatCount = 1;
// //
// // CAAnimationGroup *groupAnnimation = [CAAnimationGroup animation];
// // groupAnnimation.duration = 2.0f;
// // groupAnnimation.autoreverses = NO;// 动画在完成后是否相反执行
// // groupAnnimation.animations = @[animation];
// // groupAnnimation.repeatCount = 1;
// [self.pointLayer addAnimation:animation forKey:@"groupAnnimation"];
// // self.pointLayer.transform = CATransform3DMakeRotation(angle2, 0, 0, 1);
}
-(void)setValue:(float)value{
if(value==-1)
_valueLable.text=@"--";
else
_valueLable.text=[NSString stringWithFormat:@"%0.2f",value];
if(value<1.0){
value=1.0;
}else if(value>3.0){
value=3.0f;
}
// if(value == _value){
// return;
// }
//减去最小值,获得变化值
float valueTmp1=_value-_minValue;
float valueTmp2=value-_minValue;
float angle1 = toRad(-135)+valueTmp1*toRad(135);
float angle2 = toRad(-135)+valueTmp2*toRad(135);
self.pointLayer.transform = CATransform3DMakeRotation(angle2, 0, 0, 1);
CASpringAnimation *animation = [CASpringAnimation animationWithKeyPath:@"transform.rotation"];
animation.mass = 10.0; //质量,影响图层运动时的弹簧惯性,质量越大,弹簧拉伸和压缩的幅度越大
animation.stiffness = 3000; //刚度系数(劲度系数/弹性系数),刚度系数越大,形变产生的力就越大,运动越快
animation.damping = 100.0;//阻尼系数,阻止弹簧伸缩的系数,阻尼系数越大,停止越快
animation.initialVelocity = 5.f;//初始速率,动画视图的初始速度大小;速率为正数时,速度方向与运动方向一致,速率为负数时,速度方向与运动方向相反
animation.duration = 1.0;
animation.fromValue = @(angle1);
animation.toValue = @(angle2);
animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = NO;
animation.autoreverses = NO;
animation.timingFunction=[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
animation.repeatCount = 1;
[self.pointLayer addAnimation:animation forKey:@"groupAnnimation"];
UIColor *rawColor1 = [UIColor kh_colorWithHexString:@"37E5FE"];
UIColor *rawColor2 = [UIColor kh_colorWithHexString:@"60df5a"];
UIColor *rawColor3 = [UIColor kh_colorWithHexString:@"efdf1b"];
UIColor *rawColor4 = [UIColor kh_colorWithHexString:@"efdf1b"];
UIColor *rawColor5 = [UIColor kh_colorWithHexString:@"f54f0A"];
UIColor *resultColor=[UIColor kh_colorWithHexString:@"37E5FE"];
if(value<1.5){
resultColor = [UIColor qmui_colorFromColor:rawColor1 toColor:rawColor2 progress:1-(1.5-value)]; // 关键方法
}else if(value<2){
resultColor = [UIColor qmui_colorFromColor:rawColor2 toColor:rawColor3 progress:1-(2.0-value)]; // 关键方法
}else if(value<2.5){
resultColor = [UIColor qmui_colorFromColor:rawColor3 toColor:rawColor4 progress:1-(2.5-value)]; // 关键方法
}else if(value<=3.0){
resultColor = [UIColor qmui_colorFromColor:rawColor4 toColor:rawColor5 progress:1-(3.0-value)]; // 关键方法
}
self.pointLayer.fillColor=resultColor.CGColor;
self.bPointLayer.fillColor=resultColor.CGColor;
UIBezierPath *outArc=[UIBezierPath bezierPathWithArcCenter:_dotCenter radius:_radius-3 startAngle:_startAngle endAngle:_endAngle clockwise:YES];
_arcLayer.path=outArc.CGPath;
CASpringAnimation *animation1 = [CASpringAnimation animationWithKeyPath:@"strokeEnd"];
animation1.mass = 10.0; //质量,影响图层运动时的弹簧惯性,质量越大,弹簧拉伸和压缩的幅度越大
animation1.stiffness = 3000; //刚度系数(劲度系数/弹性系数),刚度系数越大,形变产生的力就越大,运动越快
animation1.damping = 100.0;//阻尼系数,阻止弹簧伸缩的系数,阻尼系数越大,停止越快
animation1.initialVelocity = 5.f;//初始速率,动画视图的初始速度大小;速率为正数时,速度方向与运动方向一致,速率为负数时,速度方向与运动方向相反
animation1.duration = 1.0;
animation1.fromValue = @((_value-_minValue)/(_maxValue-_minValue));
animation1.toValue = @((value-_minValue)/(_maxValue-_minValue));
animation1.fillMode = kCAFillModeForwards;
animation1.removedOnCompletion = NO;
animation1.autoreverses = NO;
animation1.timingFunction=[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
animation1.repeatCount = 1;
[_arcLayer addAnimation:animation1 forKey:@"groupAnnimation"];
_value=value;
}
@end
使用
float w=200;
WBBDashBoard *dashBoard=[[WBBDashBoard alloc]initWithFrame:CGRectMake(0,10, w, w)];
dashBoard.center= contextView.center;
[contextView addSubview:dashBoard];
我还有安卓版本: