iOS普通儀表盤的繪制

一、前言與背景

2017.9.10介紹了一種工作中需要實(shí)現(xiàn)的iOS儀表盤的繪制,不夠普通,本文介紹另一種適用性更大的儀表盤的實(shí)現(xiàn)。備注:本文無其他依賴代碼,移植和學(xué)習(xí)都很方便。
先看下運(yùn)行后的效果圖:

普通儀表盤2.png

二、需求分析

思路:可分成三步:
1.繪制三個彩色圓環(huán)帶;
2.繪制文本;
3.繪制指針。
其中第3步,指針最為復(fù)雜,是本文的重點(diǎn)。

三、實(shí)現(xiàn)

首先創(chuàng)建一個繼承自UIView的子類XRInstrumentBoard,所有的繪制都是在其內(nèi)部實(shí)現(xiàn),并開放一些接口供外部對象使用。
類聲明和外部接口:

@interface XRInstrumentBoard : UIView

@property (nonatomic, assign) CGFloat value;

- (void)strokePath;

@end

類內(nèi)部屬性:

@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

關(guān)鍵詞解釋:儀表分值、指針圓點(diǎn)、半徑、指針長度、角度數(shù)組、指針layer。

1.繪制三個彩色圓環(huán)帶

首先抽象出一個方法:返回一個給定起始角度和填充顏色的圓環(huán)。跟之前一樣,使用 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];
}

然后手動計算三條圓環(huán)的起始角度并給定好填充顏色,依次繪制。XRColorRGB:一個顏色的宏定義,默認(rèn)的綠色太辣眼睛,請無視,自行替換自己需要的顏色。

    [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循環(huán)創(chuàng)建指定數(shù)量的label即可,會用到一些高中的二維坐標(biāo)函數(shù)公式。本文使用較淺,有很多復(fù)雜的繪圖和動畫會大量使用這些公式,不熟悉的建議先補(bǔ)充,知識點(diǎn):三角函數(shù)、圓、弧度、坐標(biāo)系。

- (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];
    }
}

備注:角度和弧度轉(zhuǎn)換公式:

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

給定角度和半徑求點(diǎn)的位置:

- (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.繪制指針

下面開始本文的重點(diǎn)部分,這一步分三部分。1)繪制指針錨點(diǎn);2)繪制指針;3)添加指針動畫。

1)繪制指針錨點(diǎn)

一個封閉單色填充的圓,圓點(diǎn)初始化時會給予賦值,后面的代碼會提供。

- (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.從最右端移動到最左端。執(zhí)行次數(shù)均為1次,所有動畫執(zhí)行完成后,會回到最初也就是最終的位置上。

- (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.內(nèi)部調(diào)用邏輯

重寫、初始化相關(guān)變量默認(rèn)值。

- (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 = @[@"危險", @"普通", @"優(yōu)秀"];
        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.外部使用

創(chuàng)建一個視圖控制器,導(dǎo)入頭文件,添加視圖屬性變量,簡單的賦值即可。

#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];   
}

四、運(yùn)行動態(tài)效果圖

儀表盤動畫.gif

五、GitHub下載地址歡迎點(diǎn)贊。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容