先来表盘效果:
gif录制的不怎样。
为了实现上述的效果,我们首当其冲的是得了解的是贝塞尔曲线。
Bézier curve(贝塞尔曲线)是应用于二维图形应用程序的数学曲线.主要有起始点、终止点(也称锚点)、控制点这几个概念。通过调整控制点,贝塞尔曲线的形状会发生变化。
弧线的参考系:
分步骤实现效果:
1、画出内侧的圆弧
// 内弧线
- (void)drawInsideArc
{
//主要解释一下各个参数的意思
//center 中心点(可以理解为圆心)
//radius 半径
//startAngle 起始角度
//endAngle 结束角度
//clockwise 是否顺时针
CGFloat perAngle = M_PI / 80;
for (int i = 0; i< 81; i++) {
CGFloat startAngel = (-M_PI + perAngle * i);
CGFloat endAngel = startAngel + perAngle/3;
UIBezierPath *tickPath = [UIBezierPath bezierPathWithArcCenter:self.m_Center radius:INSIDE_RADIU startAngle:startAngel endAngle:endAngel clockwise:YES];
CAShapeLayer *perLayer = [CAShapeLayer layer];
perLayer.strokeColor = [UIColor lightGrayColor].CGColor;
perLayer.lineWidth = 1.f;
perLayer.path = tickPath.CGPath;
[self.layer addSublayer:perLayer];
}
}
2、画外侧变色表盘及刻度
- (void)drawOutsideBg
{
CGFloat perAngle = M_PI / 50;
//我们需要计算出每段弧线的起始角度和结束角度
//这里我们从- M_PI 开始,我们需要理解与明白的是我们画的弧线与内侧弧线是同一个圆心
for (int i = 0; i< 51; i++) {
CGFloat startAngel = (-M_PI + perAngle * i);
CGFloat endAngel = startAngel + perAngle;
UIBezierPath *tickPath = [UIBezierPath bezierPathWithArcCenter:self.m_Center radius:OUTSIDE_RADIU startAngle:startAngel endAngle:endAngel clockwise:YES];
CAShapeLayer *perLayer = [CAShapeLayer layer];
perLayer.lineWidth = 30.f;
perLayer.strokeColor = [self getBgColor:i].CGColor;
if (i % 5 == 0) {
[self drawOutsideScaleWithAngel:endAngel withData:i];
}
perLayer.path = tickPath.CGPath;
[self.layer addSublayer:perLayer];
}
}
// 获取渐变背景色
- (UIColor *)getBgColor:(NSInteger)value
{
float one = (255 + 255) / 60;//(255+255)除以最大取值的三分之二
int r=0,g=0,b=0;
if (value < 30)//第一个三等分
{
r = 255;
g = (int)(one * value);
}
else if (value >= 30 && value < 60)//第二个三等分
{
r = 255 - (int)((value - 30) * one);//val减最大取值的三分之一;
g = 255;
}
else { g = 255; }//最后一个三等分
return RGBA(r, g, b, 1.0);
}
// 外侧 刻度
- (void)drawOutsideScaleWithAngel:(CGFloat)textAngel withData:(int)index
{
CGPoint point = [self calculateTextPositonWithArcCenter:self.m_Center Angle:-textAngel];
NSString *tickText = [NSString stringWithFormat:@"%d",index * 2];
if (index % 10 == 0) {
tickText = [NSString stringWithFormat:@"%d",index * 2 *10];
}else{
if (index == 5) {
tickText = @"超低";
}else if(index == 15){
tickText = @"较低";
}else if(index == 25){
tickText = @"正常";
}else if(index == 35){
tickText = @"较高";
}else if(index == 45){
tickText = @"超高";
}
}
//默认label的大小30 * 14
UILabel *text = [[UILabel alloc] initWithFrame:CGRectMake(point.x - 15, point.y - 8, 30, 14)];
text.text = tickText;
text.font = [UIFont systemFontOfSize:10];
text.textColor = [UIColor colorWithRed:0.54 green:0.78 blue:0.91 alpha:1.0];
text.textAlignment = NSTextAlignmentCenter;
[self addSubview:text];
}
//默认计算半径135
- (CGPoint)calculateTextPositonWithArcCenter:(CGPoint)center
Angle:(CGFloat)angel
{
CGFloat x = 135 * cosf(angel);
CGFloat y = 135 * sinf(angel);
return CGPointMake(center.x + x, center.y - y);
}
3、绘制外部分类分割线
// 外侧弧线分割
- (void)drawOutsideArc
{
CGFloat perAngle = M_PI / 5;
//我们需要计算出每段弧线的起始角度和结束角度
//这里我们从- M_PI 开始,我们需要理解与明白的是我们画的弧线与内侧弧线是同一个圆心
for (int i = 1; i< 5; i++) {
CGFloat startAngel = (-M_PI + perAngle * i);
CGFloat endAngel = startAngel + perAngle/80;
UIBezierPath *tickPath = [UIBezierPath bezierPathWithArcCenter:self.m_Center radius:OUTSIDE_RADIU startAngle:startAngel endAngle:endAngel clockwise:YES];
CAShapeLayer *perLayer = [CAShapeLayer layer];
perLayer.lineWidth = 30.f;
perLayer.strokeColor = [UIColor whiteColor].CGColor;
perLayer.path = tickPath.CGPath;
[self.layer addSublayer:perLayer];
}
}
4、使用label方式添加表盘中央view
5、进度展示,包含进度区域及进度line
// 绘制进度填充
- (void)drawProgreeArc
{
UIBezierPath *progressPath = [UIBezierPath bezierPathWithArcCenter:self.m_Center
radius:130
startAngle:- M_PI
endAngle:0
clockwise:YES];
self.progressLayer = [CAShapeLayer layer];
self.progressLayer.lineWidth = 30.f;
self.progressLayer.fillColor = [UIColor clearColor].CGColor;
self.progressLayer.strokeColor = RGBA(185,243,110,0.2).CGColor;
self.progressLayer.path = progressPath.CGPath;
self.progressLayer.strokeStart = 0;
self.progressLayer.strokeEnd = 0;
[self.layer addSublayer:self.progressLayer];
}
// 绘制进度曲线
- (void)drawProgreeLineArc
{
UIBezierPath *progressPath = [UIBezierPath bezierPathWithArcCenter:self.m_Center
radius:118
startAngle:- M_PI
endAngle:0
clockwise:YES];
self.progessLineLayer = [CAShapeLayer layer];
self.progessLineLayer.lineWidth = 2.f;
self.progessLineLayer.fillColor = [UIColor clearColor].CGColor;
self.progessLineLayer.strokeColor = RGBA(236,92,55,1).CGColor;
self.progessLineLayer.path = progressPath.CGPath;
self.progessLineLayer.strokeStart = 0;
self.progessLineLayer.strokeEnd = 0;
[self.layer addSublayer:self.progessLineLayer];
}
6、提供接口设置评分值及时间
- (void)setM_CreditNum:(NSString *)m_CreditNum
{
_m_CreditNum = m_CreditNum;
// 实现项目逻辑
// *****
// 评分值在表盘中进度展示
CGFloat endStoke = _m_CreditNum? _m_CreditNum.floatValue/1000:0;
self.progressLayer.strokeEnd = endStoke;
self.progessLineLayer.strokeEnd = endStoke;
// 数字动画逻辑
[self creditAnimationBegin:0 End:m_CreditNum.floatValue];
}
7、评分值递增动态展示
// 数字动画
- (void)creditAnimationBegin:(int)begin End:(int)end{
NSMutableDictionary *userinfo = [NSMutableDictionary dictionary];
[userinfo setObject:@(begin) forKey:@"beginNumber"];
[userinfo setObject:@(end) forKey:@"endNumber"];
self.timer = [NSTimer scheduledTimerWithTimeInterval:1/20.0 target:self selector:@selector(changeNumberAnimation:) userInfo:userinfo repeats:YES];
}
- (void)changeNumberAnimation:(NSTimer *)timer{
int begin = [timer.userInfo[@"beginNumber"] floatValue];
int end = [timer.userInfo[@"endNumber"] floatValue];
int current = begin;
current += 50;
[timer.userInfo setObject:@(current) forKey:@"beginNumber"];
if (current >= end) {
current = end;
}
self.s_NumLabel.text = [NSString stringWithFormat:@"%d",current];
if (current == end) {
[timer invalidate];
self.timer = nil;
}
}
我们可以在外部通过设置评分值,即可实现上述评分表盘。
demo github地址:https://github.com/yinxiaoyan/Credit_Plate
待优化点:
1.填充动画
2.提供UI修改接口
非常感谢参考文档作者。
参考:https://www.jianshu.com/p/7655315620f7