美文网首页
iOS普通仪表盘的绘制

iOS普通仪表盘的绘制

作者: Brave1991 | 来源:发表于2018-03-20 11:23 被阅读0次

    一、前言与背景

    2017.9.10介绍了一种工作中需要实现的iOS仪表盘的绘制,不够普通,本文介绍另一种适用性更大的仪表盘的实现。备注:本文无其他依赖代码,移植和学习都很方便。
    先看下运行后的效果图:

    普通仪表盘2.png

    二、需求分析

    思路:可分成三步:
    1.绘制三个彩色圆环带;
    2.绘制文本;
    3.绘制指针。
    其中第3步,指针最为复杂,是本文的重点。

    三、实现

    首先创建一个继承自UIView的子类XRInstrumentBoard,所有的绘制都是在其内部实现,并开放一些接口供外部对象使用。
    类声明和外部接口:

    @interface XRInstrumentBoard : UIView
    
    @property (nonatomic, assign) CGFloat value;
    
    - (void)strokePath;
    
    @end
    

    类内部属性:

    @interface XRInstrumentBoard ()
    
    @property (nonatomic, assign) CGPoint dotCenter;
    @property (nonatomic, assign) CGFloat radius;
    @property (nonatomic, assign) CGFloat pointLenth;
    @property (nonatomic, strong) NSArray *stateArray;
    @property (nonatomic, strong) CALayer *pointLayer;
    
    @end
    

    关键词解释:仪表分值、指针圆点、半径、指针长度、角度数组、指针layer。

    1.绘制三个彩色圆环带

    首先抽象出一个方法:返回一个给定起始角度和填充颜色的圆环。跟之前一样,使用 CAShapeLaye配合UIBezierPath即可在layer层完成绘制。

    - (void)drawPieWithStartAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle color:(UIColor *)color {
        UIBezierPath *piePath = [UIBezierPath bezierPath];
        [piePath addArcWithCenter:self.dotCenter radius:self.radius startAngle:toRad(startAngle) endAngle:toRad(endAngle) clockwise:YES];
        
        CAShapeLayer *pieShapeLayer = [[CAShapeLayer alloc] init];
        pieShapeLayer.lineWidth = 10;
        pieShapeLayer.fillColor = nil;
        pieShapeLayer.strokeColor = color.CGColor;
        pieShapeLayer.path = [piePath CGPath];;
        [self.layer addSublayer:pieShapeLayer];
    }
    

    然后手动计算三条圆环的起始角度并给定好填充颜色,依次绘制。XRColorRGB:一个颜色的宏定义,默认的绿色太辣眼睛,请无视,自行替换自己需要的颜色。

        [self drawPieWithStartAngle:-180 endAngle:-120 color:[UIColor redColor]];
        [self drawPieWithStartAngle:-120 endAngle:-60 color:[UIColor orangeColor]];
        [self drawPieWithStartAngle:-60 endAngle:0 color:XRColorRGB(142, 195, 92)];
    

    2.绘制文本

    使用for循环创建指定数量的label即可,会用到一些高中的二维坐标函数公式。本文使用较浅,有很多复杂的绘图和动画会大量使用这些公式,不熟悉的建议先补充,知识点:三角函数、圆、弧度、坐标系。

    - (void)drawText {
        for (NSInteger i=0; i<3; i++) {
            CGFloat startAngle = -150 + 60*i;
            CGPoint labelCenter = [self pointWithAngle:toRad(startAngle) radius:self.radius + 30];
            
            UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 40, 40)];
            label.center = labelCenter;
            label.font = XRFont(14);
            label.backgroundColor = XRTextBlueColor;
            label.textColor = [UIColor whiteColor];
            label.textAlignment = NSTextAlignmentCenter;
            label.text = self.stateArray[i];
            label.layer.cornerRadius = 20;
            label.layer.masksToBounds = YES;
            [self addSubview:label];
        }
    }
    

    备注:角度和弧度转换公式:

    #define toRad(angle) ((angle) * M_PI / 180)
    

    给定角度和半径求点的位置:

    - (CGPoint)pointWithAngle:(CGFloat)angle radius:(CGFloat)radius {
        CGFloat x = self.dotCenter.x + cosf(angle) * radius;
        CGFloat y = self.dotCenter.y + sinf(angle) * radius;
        return CGPointMake(x, y);
    }
    

    3.绘制指针

    下面开始本文的重点部分,这一步分三部分。1)绘制指针锚点;2)绘制指针;3)添加指针动画。

    1)绘制指针锚点

    一个封闭单色填充的圆,圆点初始化时会给予赋值,后面的代码会提供。

    - (void)drawDot {
        UIBezierPath *piePath = [UIBezierPath bezierPath];
        [piePath addArcWithCenter:self.dotCenter radius:10 startAngle:0 endAngle:2*M_PI clockwise:YES];
        
        CAShapeLayer *pieShapeLayer = [[CAShapeLayer alloc] init];
        pieShapeLayer.strokeColor = nil;
        pieShapeLayer.fillColor = XRTextBlueColor.CGColor;
        pieShapeLayer.path = [piePath CGPath];;
        [self.layer addSublayer:pieShapeLayer];
    }
    

    2)绘制指针

    由于后面需要添加摆动动画,所以将指针layer定义成属性。

    - (void)drawPoint {
        self.pointLayer = [CALayer layer];
        self.pointLayer.backgroundColor = XRTextBlueColor.CGColor;
        self.pointLayer.frame = CGRectMake(0, 0, 2, self.pointLenth);
        self.pointLayer.position = CGPointMake(self.dotCenter.x, self.dotCenter.y);
        self.pointLayer.anchorPoint = CGPointMake(0.8, 0.8);
        [self.layer addSublayer:self.pointLayer];
    }
    

    3)添加指针动画

    这里使用CAAnimationGroup将给定的CABasicAnimation对象添加为一组动画。原理与电影的制作相同。这里的动画组分2个场景:1.从最终位置移动到最右端;2.从最右端移动到最左端。执行次数均为1次,所有动画执行完成后,会回到最初也就是最终的位置上。

    - (void)strokePath {
        CGFloat diff = (self.value - 50)/100*M_PI;
        self.pointLayer.transform = CATransform3DMakeRotation(diff, 0, 0, 1);
    
        CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
        animation.duration = 1.0f;
        animation.fromValue = @(diff);
        animation.toValue = @(M_PI_2);
        animation.fillMode = kCAFillModeForwards;
        animation.removedOnCompletion = NO;
        animation.repeatCount = 1;
        
        CABasicAnimation *animation2 = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
        animation2.duration = 1.0f;
        animation2.fromValue = @(M_PI_2);
        animation2.toValue = @(-M_PI_2);
        animation2.fillMode = kCAFillModeForwards;
        animation2.removedOnCompletion = NO;
        animation2.repeatCount = 1;
        
        CAAnimationGroup *groupAnnimation = [CAAnimationGroup animation];
        groupAnnimation.duration = 1.0f;
        groupAnnimation.autoreverses = YES;
        groupAnnimation.animations = @[animation, animation2];
        groupAnnimation.repeatCount = 1;
        [self.pointLayer addAnimation:groupAnnimation forKey:@"groupAnnimation"];
    }
    

    4.内部调用逻辑

    重写、初始化相关变量默认值。

    - (instancetype)initWithFrame:(CGRect)frame {
        if (self = [super initWithFrame:frame]) {
            self.dotCenter = CGPointMake(frame.size.width/2.0, frame.size.height-20);
            self.radius = frame.size.height - 80;
            self.pointLenth = self.radius;
            self.stateArray = @[@"危险", @"普通", @"优秀"];
            self.value = 50;
            [self loadSubViews];
        }
        return self;
    }
    
    - (void)loadSubViews {
        [self drawPieWithStartAngle:-180 endAngle:-120 color:[UIColor redColor]];
        [self drawPieWithStartAngle:-120 endAngle:-60 color:[UIColor orangeColor]];
        [self drawPieWithStartAngle:-60 endAngle:0 color:XRColorRGB(142, 195, 92)];
        
        [self drawText];
        [self drawDot];
        [self drawPoint];
    }
    

    5.外部使用

    创建一个视图控制器,导入头文件,添加视图属性变量,简单的赋值即可。

    #import "XRInstumentBoardViewController.h"
    #import "XRInstrumentBoard.h"
    
    @interface XRInstumentBoardViewController ()
    
    @property (nonatomic, strong) XRInstrumentBoard *instrumentBoard;
    
    @end
    
    @implementation XRInstumentBoardViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.view.backgroundColor = [UIColor whiteColor];
        // Do any additional setup after loading the view.
        self.instrumentBoard = [[XRInstrumentBoard alloc] initWithFrame:CGRectMake(0, 0, 300, 200)];
        self.instrumentBoard.center = self.view.center;
        [self.view addSubview:self.instrumentBoard];
        
        self.instrumentBoard.value = 75;
        [self.instrumentBoard strokePath];   
    }
    

    四、运行动态效果图

    仪表盘动画.gif

    五、GitHub下载地址欢迎点赞。

    相关文章

      网友评论

          本文标题:iOS普通仪表盘的绘制

          本文链接:https://www.haomeiwen.com/subject/dgvvqftx.html