ObjC贝塞尔曲线实现渐变带动画的信用圆环

ObjC贝塞尔曲线实现渐变带动画的信用圆环,参考示例代码:

WangCreditView.h:

#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

/**
 * 信用圆环
 */
@interface WangCreditView : UIView

/** 当前显示的值(以及刻度值) */
@property(nonatomic, assign) float currentScaleVaue;

/** 刻度最大值(默认 1000) */
@property(nonatomic, assign) float maxScaleVaue;

/** 刻度最小值(默认 0) */
@property(nonatomic, assign) float minScaleVaue;

/** 评估时间(默认当前时间) */
@property(nonatomic, copy) NSString *strAssessDate;

/** 描述信息(为空不显示,默认为空) */
@property(nonatomic, copy) NSString *strDescription;

/** 内外环中间间距(默认 15.0) */
@property(nonatomic, assign) CGFloat lineMargin;

/** 开口角度 */
@property(nonatomic, assign) float startAngle;

/** 闭合角度 */
@property(nonatomic, assign) float endAngle;

//
/** 内线宽(默认 1.0) */
@property(nonatomic, assign) CGFloat inLineWidth;

/** 内线颜色(默认 白色) */
@property(nonatomic,assign) UIColor *inLineColor;


/** 外虚线宽(默认 5.0) */
@property(nonatomic, assign) CGFloat outLineWidth;

/** 外虚线颜色(默认 白色) */
@property(nonatomic,assign) UIColor *outLineColor;

/** 初始化后,调用此方法绘制(动画效果) */
-(void)startAnimationDraw;

/** 初始化后,调用此方法绘制(与上面方法二选一即可) */
-(void)startDraw;


@end

NS_ASSUME_NONNULL_END

WangCreditView.m:

#import "WangCreditView.h"

//当前中心显示值字体、颜色
#define current_font_size 25.0
#define current_font_color [UIColor whiteColor]

//当前描述信息
#define description_font_size 14.0
#define description_font_color [UIColor whiteColor]

//当前评估信息
#define assess_font_size 14.0
#define assess_font_color [UIColor whiteColor]

//当前视图宽、高
#define view_width self.frame.size.width
#define view_height self.frame.size.height

#define layer_gradient_view_tag 12345

@implementation WangCreditView{
    //当前仪表值
    UILabel *labCurrentInfo;
    
    //描述信息
    UILabel *labDescription;
    
    //评估时间
    UILabel *labAssessDate;
    
    //内线
    CAShapeLayer *inLineLayer;
    CAShapeLayer *inLineLayerBG;
    
    //外虚线
    CAShapeLayer *outLineLayer;
    CAShapeLayer *outLineLayerBG;
    CALayer *outGradientLayer;
    
    //动画
    CABasicAnimation *animation;
    
    //最小刻度
    UILabel *labMinInfo;
    
    //最大刻度
    UILabel *labMaxInfo;
    
    BOOL isAnimation;
}

-(instancetype)initWithFrame:(CGRect)frame{
    if (self = [super initWithFrame:frame]) {
        [self initView];
    }
    
    return self;
}

-(instancetype)initWithCoder:(NSCoder *)aDecoder{
    if (self = [super initWithCoder:aDecoder]) {
        [self initView];
    }
    
    return self;
}

-(void)dealloc{
    NSLog(@"dealloc");
    [self unInitObserver];
    [self clearLayer];
}


//MARK: - initView
-(void)initView{
    CGFloat h;
    CGFloat w;
    CGFloat x;
    CGFloat y;
    CGRect rect;
    
    //MARK:当前仪表值
    if (!labCurrentInfo) {
        h = 21.0;
        w = 100.0;
        x = (self.frame.size.width - w) * 0.5;
        y = (self.frame.size.height - h) * 0.5;
        rect = CGRectMake(x, y, w, h);
        
        labCurrentInfo = [[UILabel alloc] initWithFrame:rect];
        labCurrentInfo.font = [UIFont boldSystemFontOfSize:current_font_size];
        labCurrentInfo.textColor = current_font_color;
        labCurrentInfo.adjustsFontSizeToFitWidth = YES;
        labCurrentInfo.textAlignment = NSTextAlignmentCenter;
        
        [self addSubview:labCurrentInfo];
    }
    
    //MARK:当前描述信息
    if (!labDescription) {
        h = 21.0;
        w = 100.0;
        x = (self.frame.size.width - w) * 0.5;
        y = labCurrentInfo.frame.origin.y + labCurrentInfo.frame.size.height + 10.0;
        rect = CGRectMake(x, y, w, h);
        
        labDescription = [[UILabel alloc] initWithFrame:rect];
        labDescription.font = [UIFont systemFontOfSize:description_font_size];
        labDescription.textColor = description_font_color;
        labDescription.adjustsFontSizeToFitWidth = YES;
        labDescription.textAlignment = NSTextAlignmentCenter;
        
        [self addSubview:labDescription];
    }
    
    //MARK:评估时间
    if (!labAssessDate) {
        h = 21.0;
        w = 100.0;
        x = (self.frame.size.width - w) * 0.5;
        y = self.frame.size.height - h - 10.0;
        rect = CGRectMake(x, y, w, h);
        
        labAssessDate = [[UILabel alloc] initWithFrame:rect];
        labAssessDate.font = [UIFont systemFontOfSize:assess_font_size];
        labAssessDate.textColor = assess_font_color;
        labAssessDate.adjustsFontSizeToFitWidth = YES;
        labAssessDate.textAlignment = NSTextAlignmentCenter;
        
        [self addSubview:labAssessDate];
    }
    
    //监听
    [self initObserver];
    
    //[S] 初始值
    self.inLineWidth = 1.0;
    self.outLineWidth = 5.0;
    self.lineMargin = 15.0;
    self.inLineColor = [UIColor whiteColor];
    self.outLineColor = [UIColor whiteColor];
    
    self.maxScaleVaue = 1000.0;
    self.minScaleVaue = 0.0;
    
    self.startAngle = 0.785 * M_PI;
    self.endAngle = 0.230 * M_PI;
    
    self.strDescription = @"";
    self.strAssessDate = [NSString stringWithFormat:@"评估 %@",[Utils getCurrentDateToString:@"yyyy-MM-dd"]];
    //[E] 初始值
}

//MARK: - initObserver
-(void)initObserver{
    //当前值
    [self addObserver:self forKeyPath:@"currentScaleVaue" options:NSKeyValueObservingOptionNew context:nil];
    
    //评估时间
    [self addObserver:self forKeyPath:@"strAssessDate" options:NSKeyValueObservingOptionNew context:nil];
    
    //描述信息
    [self addObserver:self forKeyPath:@"strDescription" options:NSKeyValueObservingOptionNew context:nil];
}

-(void)unInitObserver{
    [self removeObserver:self forKeyPath:@"currentScaleVaue"];
    [self removeObserver:self forKeyPath:@"strAssessDate"];
    [self removeObserver:self forKeyPath:@"strDescription"];
}

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    
    NSLog(@"%@",change);
    id _newValue = [change valueForKey:@"new"];
    
    //当前值
    if ([keyPath isEqualToString:@"currentScaleVaue"]) {
        self->labCurrentInfo.text = [NSString stringWithFormat:@"%.f",[_newValue floatValue]];
    }
    //评估时间
    else if([keyPath isEqualToString:@"strAssessDate"]){
        self->labAssessDate.text = _newValue;
    }
    //描述信息
    else if([keyPath isEqualToString:@"strDescription"]){
        self->labDescription.text = _newValue;
    }
}


//MARK: - drawRect
/** 初始化后,调用此方法绘制(动画效果) */
-(void)startAnimationDraw{
    [self clearLayer];
    
    //初始化动画
    if (!animation) {
        animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
        animation.duration = 3.0;
        animation.fromValue = @0.0;
        animation.toValue = @1.0;
        animation.repeatCount = 1;
    }
    
    isAnimation = YES;
    
    [self drayinLineLayer];
    [self drayOutLineLayer];
    [self drayCalibration];
}

/** 初始化后,调用此方法绘制(与上面方法二选一即可) */
-(void)startDraw{
    [self clearLayer];
    
    //清除动画
    [self clearAnimation];
    
    isAnimation = NO;
    
    [self drayinLineLayer];
    [self drayOutLineLayer];
    [self drayCalibration];
}

//清除
-(void)clearLayer{
    
    [self clearAnimation];
    
    if (inLineLayer) {
        [inLineLayer removeFromSuperlayer];
        inLineLayer = nil;
    }
    
    if (inLineLayerBG) {
        [inLineLayerBG removeFromSuperlayer];
        inLineLayerBG = nil;
    }
    
    if (outLineLayer) {
        [outLineLayer removeFromSuperlayer];
        outLineLayer = nil;
    }
    
    if (outLineLayerBG) {
        [outLineLayerBG removeFromSuperlayer];
        outLineLayerBG = nil;
    }
    
    if (labMinInfo) {
        [labMinInfo removeFromSuperview];
        labMinInfo = nil;
    }
    
    if (labMaxInfo) {
        [labMaxInfo removeFromSuperview];
        labMaxInfo = nil;
    }
    
    if (outGradientLayer) {
        [outGradientLayer removeFromSuperlayer];
        outGradientLayer = nil;
    }
}

-(void)clearAnimation{
    if (animation) {
        animation = nil;
    }
}

//根据当前刻度值获取闭口角度
-(CGFloat)getEndAngle{
    
    //参考:https://developer.apple.com/documentation/uikit/uibezierpath/1624358-bezierpathwitharccenter?language=objc
    if (self.currentScaleVaue >= self.maxScaleVaue) {
        return self.endAngle;
    }
    else if(self.currentScaleVaue <= self.minScaleVaue){
        return self.startAngle;
    }
    else{
        CGFloat _bl = (self.currentScaleVaue - self.minScaleVaue) / (self.maxScaleVaue - self.minScaleVaue);
        return M_PI + M_PI * _bl;
    }
}


//MARK: - 绘制刻度
/** 绘制刻度 */
-(void)drayCalibration{

    CGRect rect = CGRectMake(0, 0, 40, 12);
    UIFont *font = [UIFont systemFontOfSize:10];
    UIColor *color = [UIColor whiteColor];
    
    //最小值
    if(!labMinInfo){
        rect.origin = labAssessDate.frame.origin;
        rect.origin.y -= 2.25 * rect.size.height;
        rect.origin.x -= 1.15 * self.lineMargin;
        
        labMinInfo = [[UILabel alloc] initWithFrame:rect];
        labMinInfo.text = [NSString stringWithFormat:@"%.f",self.minScaleVaue];
        labMinInfo.textColor = color;
        labMinInfo.font = font;
        labMinInfo.textAlignment = NSTextAlignmentCenter;
        
        //顺时针旋转(- 逆时针旋转)
        labMinInfo.transform = CGAffineTransformMakeRotation(-M_PI*0.65);
        
        [self addSubview:labMinInfo];
    }
    
    //最大值
    if(!labMaxInfo){
        rect.origin = labAssessDate.frame.origin;
        rect.origin.y -= 2.45 * rect.size.height;
        rect.origin.x += 3.75 * self.lineMargin + rect.size.width;
        
        labMaxInfo = [[UILabel alloc] initWithFrame:rect];
        labMaxInfo.text = [NSString stringWithFormat:@"%.f",self.maxScaleVaue];
        labMaxInfo.textColor = color;
        labMaxInfo.font = font;
        labMaxInfo.textAlignment = NSTextAlignmentCenter;
        
        //顺时针旋转(- 逆时针旋转)
        labMaxInfo.transform = CGAffineTransformMakeRotation(-M_PI*0.35);
        
        [self addSubview:labMaxInfo];
    }
}


//MARK: - 绘制内线及背景
/**
 * 绘制内线
 * 参考:https://www.cnblogs.com/ioshe/p/5481841.html
 */
-(void)drayinLineLayer{
    if (!inLineLayer) {
        inLineLayer = [CAShapeLayer layer];
        
        if (animation) {
            [inLineLayer addAnimation:animation forKey:@"strokeEndAnimation"];
        }
    }

    CGFloat x = 0.0;
    CGFloat y = 0.0;
    CGFloat w = view_width - 2 * self.outLineWidth - 4 * self.lineMargin;
    CGFloat h = labAssessDate.frame.origin.y - 3 * self.lineMargin - self.outLineWidth;
    CGRect rect = CGRectMake(x, y, w, h);
    
    inLineLayer.frame = rect;
    inLineLayer.position = CGPointMake(view_width * 0.5, view_height * 0.5);
    
    //填充颜色(此处为透明填充)
    inLineLayer.fillColor = [UIColor clearColor].CGColor;
    
    //线条宽度
    inLineLayer.lineWidth = self.inLineWidth;
    
    //线条颜色
    inLineLayer.strokeColor = self.inLineColor.CGColor;
    
    //设置stroke起始点
    //参考:https://blog.csdn.net/yixiangboy/article/details/50662704
    //inLineLayer.strokeStart = 0.38;
    //inLineLayer.strokeEnd = 1.0;
    
    //创建出圆形贝塞尔曲线
    UIBezierPath *bezierPath = [UIBezierPath bezierPathWithArcCenter:[self getCenterPoint:1]
                                                        radius:rect.size.height * 0.5
                                                    startAngle:_startAngle
                                                      endAngle:[self getEndAngle]
                                                     clockwise:YES];// 是否顺时针方向
    bezierPath.lineCapStyle = kCGLineCapRound;
    bezierPath.lineJoinStyle = kCGLineJoinRound;
    
    //按照塞尔曲线绘制
    inLineLayer.path = bezierPath.CGPath;
    
    // [S] 绘制背景
    inLineLayerBG = [CAShapeLayer layer];
    inLineLayerBG.frame = inLineLayer.frame;
    inLineLayerBG.position = inLineLayer.position;
    inLineLayerBG.fillColor = [UIColor clearColor].CGColor;
    inLineLayerBG.lineWidth = self.inLineWidth;
    inLineLayerBG.strokeColor = [UIColor lightGrayColor].CGColor;
    
    bezierPath = [UIBezierPath bezierPathWithArcCenter:[self getCenterPoint:1]
                                                radius:rect.size.height * 0.5
                                            startAngle:_startAngle
                                              endAngle:_endAngle
                                             clockwise:YES];
    bezierPath.lineCapStyle = kCGLineCapRound;
    bezierPath.lineJoinStyle = kCGLineJoinRound;
    inLineLayerBG.path = bezierPath.CGPath;
    // [E] 绘制背景
    
    [self.layer addSublayer:inLineLayerBG];
    [self.layer addSublayer:inLineLayer];
}


//MARK: - 绘制外虚线及背景
-(void)drayOutLineLayer{
    if (!outLineLayer) {
        outLineLayer = [CAShapeLayer layer];
        
        if (animation) {
            [outLineLayer addAnimation:animation forKey:@"strokeEndAnimation"];
        }
    }
    
    CGFloat x = 0.0;
    CGFloat y = 0.0;
    CGFloat w = view_width - 2 * self.lineMargin;
    CGFloat h = labAssessDate.frame.origin.y;
    CGRect rect = CGRectMake(x, y, w, h);
    
    outLineLayer.frame = rect;
    outLineLayer.position = CGPointMake(view_width * 0.5, view_height * 0.5);
    
    //填充颜色(此处为透明填充)
    outLineLayer.fillColor = [UIColor clearColor].CGColor;
    
    //线条宽度
    outLineLayer.lineWidth = self.outLineWidth;
    //[outLineLayer setLineJoin:kCALineJoinRound];
    
    //1=线的宽度 3=每条线的间距
    NSArray *_temp = [NSArray arrayWithObjects:@1,@3,nil];
    [outLineLayer setLineDashPattern:_temp];
    
    //线条颜色
    outLineLayer.strokeColor = self.outLineColor.CGColor;
    
    //设置stroke起始点
    //参考:https://blog.csdn.net/yixiangboy/article/details/50662704
    //outLineLayer.strokeStart = 0.50;
    //outLineLayer.strokeEnd = 1.0;
    
    //创建出圆形贝塞尔曲线
    UIBezierPath *bezierPath = [UIBezierPath bezierPathWithArcCenter:[self getCenterPoint:2]
                                                              radius:rect.size.height * 0.5
                                                          startAngle:_startAngle
                                                            endAngle:[self getEndAngle]
                                                           clockwise:YES];
    
    //按照贝塞尔曲线绘制
    outLineLayer.path = bezierPath.CGPath;
    
    // [S] 绘制背景
    outLineLayerBG = [CAShapeLayer layer];
    outLineLayerBG.frame = outLineLayer.frame;
    outLineLayerBG.position = outLineLayer.position;
    outLineLayerBG.fillColor = [UIColor clearColor].CGColor;
    outLineLayerBG.lineWidth = self.outLineWidth;
    outLineLayerBG.strokeColor = [UIColor lightGrayColor].CGColor;
    
    bezierPath = [UIBezierPath bezierPathWithArcCenter:[self getCenterPoint:2]
                                                radius:rect.size.height * 0.5
                                            startAngle:_startAngle
                                              endAngle:_endAngle
                                             clockwise:YES];
    
    [outLineLayerBG setLineJoin:kCALineJoinRound];
    [outLineLayerBG setLineDashPattern:_temp];
    outLineLayerBG.path = bezierPath.CGPath;
    // [E] 绘制背景
    
    [self.layer addSublayer:outLineLayerBG];
    //非渐变填充
    //[self.layer addSublayer:outLineLayer];
    
    //渐变填充
    outGradientLayer = [self drayColor:outLineLayer];
    [self.layer addSublayer:outGradientLayer];
}

//type 1 内线 2 外线
-(CGPoint)getCenterPoint:(NSInteger)type{
    CGPoint point = CGPointMake(view_width * 0.5, view_height * 0.5);
    
    //内线
    if (type == 1) {
        point.x -= 23.0;
        point.y -= 23.0;
    }
    //外线
    else if (type == 2){
        point.x -= 10.0;
        point.y -= 10.0;
    }
    
    return point;
}


//MARK: - 绘制渐变色
-(CALayer *)drayColor:(CAShapeLayer *)circle{
    
    //创建第一个gradientLayer
    NSArray *colors1 = @[
                 (__bridge id)[UIColor colorWithRed:49.f/255.f green:240.f/255.f blue:233.f/255.f alpha:1.0].CGColor,
                 (__bridge id)[UIColor colorWithRed:69.f/255.f green:240.f/255.f blue:210.f/255.f alpha:1.0].CGColor,
                 (__bridge id)[UIColor colorWithRed:143.f/255.f green:238.f/255.f blue:147.f/255.f alpha:1.0].CGColor,
                 (__bridge id)[UIColor colorWithRed:220.f/255.f green:237.f/255.f blue:92.f/255.f alpha:1.0].CGColor,
                 (__bridge id)[UIColor colorWithRed:253.f/255.f green:237.f/255.f blue:74.f/255.f alpha:1.0].CGColor
    ];

    CGRect frame1 = CGRectMake(0.0, 0.0, circle.frame.size.width * 0.5, circle.frame.size.height);
    CAGradientLayer *gradientLayer1 = [CAGradientLayer layer];
    gradientLayer1.startPoint = CGPointMake(0.5, 0.0);
    gradientLayer1.endPoint = CGPointMake(0.5, 1.0);
    gradientLayer1.frame = frame1;
    gradientLayer1.colors = colors1;

    NSArray *colors2 = @[
                 (__bridge id)[UIColor colorWithRed:253.f/255.f green:237.f/255.f blue:74.f/255.f alpha:1.0].CGColor,
                 (__bridge id)[UIColor colorWithRed:224.f/255.f green:238.f/255.f blue:59.f/255.f alpha:1.0].CGColor,
                 (__bridge id)[UIColor colorWithRed:148.f/255.f green:238.f/255.f blue:146.f/255.f alpha:1.0].CGColor
    ];

    CGRect frame2 = CGRectMake(circle.frame.size.width * 0.5, 0.0, circle.frame.size.width * 0.5, circle.frame.size.height);
    CAGradientLayer *gradientLayer2 = [CAGradientLayer layer];
    gradientLayer2.startPoint = CGPointMake(0.5, 1.0);
    gradientLayer2.endPoint = CGPointMake(0.5, 1.0);
    gradientLayer2.frame = frame2;
    gradientLayer2.colors = colors2;
    
    //用一个gradientLayer来承接这两个渐变区域
    CALayer *gradientLayer = [CALayer layer];
    gradientLayer.frame = circle.bounds;
    [gradientLayer addSublayer:gradientLayer1];
    [gradientLayer addSublayer:gradientLayer2];

    //用circle的path形状来截取渐变区域
    gradientLayer.mask = circle;
    
    return gradientLayer;
}

@end

调用实例:

//仪表
@property (nonatomic,strong) WangCreditView *creditView;

//MARK: - 信用仪表盘
-(WangCreditView *)creditView{
    if (!_creditView) {
        CGFloat y = 0.0;
        CGFloat h = table_head_view_height - split_line_height;
        CGFloat w = h;
        CGFloat x = (K_APP_WIDTH - w) * 0.5;
        CGRect rect = CGRectMake(x, y, w, h);
        _creditView = [[WangCreditView alloc] initWithFrame:rect];
        _creditView.backgroundColor = [UIColor clearColor];
        
        _creditView.minScaleVaue = 0.0;
        _creditView.maxScaleVaue = 350.0;
        
        _creditView.lineMargin = 10.0;
        _creditView.strAssessDate = [NSString stringWithFormat:@"评估 %@",[Utils getCurrentDateToString:@"yyyy-MM-dd"]];
        _creditView.strDescription = @"信用小白";
        
        //获取值
        [self updateCreditView];
        
        //开始绘制
        [_creditView startAnimationDraw];
    }
    
    return _creditView;
}

-(void)updateCreditView{
    
    NSString *strInfo = @"信用小白";
    CGFloat floatValue = 0.0;
    
    if ([self userIsLogin]) {
        NSInteger i = 0;

        if (K_APP_R_STATUS_PASS) i++;
        if (K_APP_Y_STATUS_PASS) i++;
        if (K_APP_BANK_STATUS_PASS) i++;
        if (K_APP_ZFB_STATUS_PASS) i++;
        if (K_APP_GRXX_STATUS_PASS) i++;

        floatValue = i * 50.0;
        if(floatValue <= 50){
            strInfo = @"信用小白";
        }
        else if (floatValue <= 200) {
            strInfo = @"信用良好";
        }
        else{
            strInfo = @"信用优秀";
        }
    }
    
    self.creditView.currentScaleVaue = floatValue;
    self.creditView.strDescription = strInfo;
}

效果图如下:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

追夢秋陽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值