绘图-圆环进度条实现详解

前言

实现了一款时下比较流行的环状进度动图,以下是源码解析


使用 Core Graphics 和 定时器 实现环形进度动图

圆环进度.gif

核心源码

# 使用  [self setNeedsDisplay];  会触发drawRect 方法达到刷新视图的效果。
//画背景线、填充线、小圆点、文字
- (void)drawRect:(CGRect)rect {
    [super drawRect:rect];
    
    
    #获取图形上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    
    #设置中心点 半径 起点及终点
    CGFloat maxWidth = self.frame.size.width<self.frame.size.height?self.frame.size.width:self.frame.size.height;
    CGPoint center = CGPointMake(maxWidth/2.0, maxWidth/2.0);
    CGFloat radius = maxWidth/2.0-_strokeWidth/2.0-1;//留出一像素,防止与边界相切的地方被切平
    CGFloat endA = _startAngle + (CircleDegreeToRadian(360) - _reduceValue);//圆终点位置
    CGFloat valueEndA = _startAngle + (CircleDegreeToRadian(360)-_reduceValue)*fakeProgress;  //数值终点位置
    
    #灰色区域背景
    UIBezierPath *basePath = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:_startAngle endAngle:endA clockwise:YES];
    //线条宽度
    CGContextSetLineWidth(ctx, _strokeWidth);
    //设置线条顶端
    CGContextSetLineCap(ctx, kCGLineCapRound);
    //线条颜色
    [_pathBackColor setStroke];
    //把路径添加到上下文
    CGContextAddPath(ctx, basePath.CGPath);
    //渲染背景线
    CGContextStrokePath(ctx);
    
    #彩色动态区域
    UIBezierPath *valuePath = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:_startAngle endAngle:valueEndA clockwise:YES];
    //设置线条顶端
    CGContextSetLineCap(ctx, kCGLineCapRound);
    //线条颜色
    [_pathFillColor setStroke];
    //把路径添加到上下文
    CGContextAddPath(ctx, valuePath.CGPath);
    //渲染数值线
    CGContextStrokePath(ctx);
    
   #画小圆点 使用是 绘制一张图片,其中图片的 Frame 中 x,y 使用到 正弦,余弦函数得到。
    if (_showPoint) {
        CGContextDrawImage(ctx, CGRectMake(CircleSelfWidth/2 + radius*cosf(valueEndA)-_strokeWidth/2.0, CircleSelfWidth/2 + radius*sinf(valueEndA)-_strokeWidth/2.0, _strokeWidth, _strokeWidth), [UIImage imageNamed:@"circle_point"].CGImage);
    }
   # 绘制中间的文字,由于定时器的持续执行刷新,就出现了,文字跳动累加的效果。
   # 其实直接在这里 修改一个全局 label 的值效果是一样的。
    if (_showProgressText) {
        //画文字
        NSString *currentText = [NSString stringWithFormat:@"%.2f%%",fakeProgress*100];
        //段落格式
        NSMutableParagraphStyle *textStyle = [[NSMutableParagraphStyle defaultParagraphStyle] mutableCopy];
        textStyle.lineBreakMode = NSLineBreakByWordWrapping;
        textStyle.alignment = NSTextAlignmentCenter;//水平居中
        //字体
        UIFont *font = [UIFont boldSystemFontOfSize:22.0];
        //构建属性集合
        NSDictionary *attributes = @{NSFontAttributeName:font, NSParagraphStyleAttributeName:textStyle};
        //获得size
        CGSize stringSize = [currentText sizeWithAttributes:attributes];
        //垂直居中
        CGRect r = CGRectMake((CircleSelfWidth-stringSize.width)/2.0, (CircleSelfHeight - stringSize.height)/2.0,stringSize.width, stringSize.height);
        [currentText drawInRect:r withAttributes:attributes];
    }
    
}

小球图片的坐标计算参照以下这张图

Paste_Image.png

#设置进度 属性的 set 方法
- (void)setProgress:(CGFloat)progress {
    
    _progress = progress;
    fakeProgress = 0.0;
    
    if (timer) {
        [timer invalidate];
        timer = nil;
    }
    
    //如果为0则直接刷新
    if (_progress == 0.0) {
        [self setNeedsDisplay];
        return;
    }
    #生成一个定时器 持续触发  drawRect 方法
    timer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selector(asa ) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
    
}
#定时器绑定的方法
- (void)asa
{
    if (fakeProgress >= _progress || fakeProgress >= 1.0f) {
        #最后一次赋准确值 并移除定时器
        fakeProgress = _progress;
        [self setNeedsDisplay];
        
        if (timer) {
            [timer invalidate];
            timer = nil;
        }
        return;
    } else {
        #进度条动画
        [self setNeedsDisplay];
    }
    
    # fakeProgress 数值增加
    if (_animationModel == CircleIncreaseSameTime) {
        fakeProgress += 0.01*(_progress);//不同进度动画时间基本相同
    } else {
        fakeProgress += 0.01;//进度越大动画时间越长。
    }
}

使用 CAShapeLayer 和 CABasicAnimation 实现环形进度动图

</br>

进度.gif

核心源码

# 橘红色的背景
CAShapeLayer *shapeLayer11=[CAShapeLayer layer];
UIBezierPath*path12=[UIBezierPath bezierPathWithOvalInRect:CGRectMake(89, 299, 138, 138)];
# 设置末端的形状为圆形
path12.lineCapStyle = kCGLineCapRound;  
shapeLayer11.path=path12.CGPath;
# 就是图中我们看到的橘红色   CAShapeLayer的fillColor 填充的是 贝塞尔曲线的有效区域   半径为 138的圆
shapeLayer11.fillColor=[UIColor colorWithRed:0.95 green:0.37 blue:0.38 alpha:1.00].CGColor;
# 设置lineWidth 为0
shapeLayer11.lineWidth=0;
 # lineWidth 为0 strokeColor没有效果
//shapeLayer11.strokeColor=[UIColor colorWithRed:0.95 green:0.37 blue:0.38 alpha:1.00].CGColor;
[self.layer addSublayer:shapeLayer11];

# 深绿色的区域
CAShapeLayer *shapeLayer1 = [CAShapeLayer layer];
UIBezierPath *path1 = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(89, 299, 138, 138)];
path1.lineCapStyle = kCGLineJoinRound;
shapeLayer1.path = path1.CGPath;
# 设置内部填充色为 无色
shapeLayer1.fillColor = [UIColor clearColor].CGColor; 
# 设计 lineWidth 为20 (深绿色区域)我们可以看到是绿色区域的中心线在贝塞尔曲线的边界上
shapeLayer1.lineWidth = 20;
shapeLayer1.strokeColor = [UIColor colorWithRed:0.16 green:0.58 blue:0.22 alpha:1.00].CGColor;
[self.layer addSublayer:shapeLayer1];

# strokeEnd为CAShapeLayer的终点,这里设置为动画的 KEY
CABasicAnimation *pathAnima = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
pathAnima.duration = 1.5f;
pathAnima.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
pathAnima.fromValue = [NSNumber numberWithFloat:0.0f]; 
# 终点值设置为 0.8 
pathAnima.toValue = [NSNumber numberWithFloat:.8f];
pathAnima.fillMode = kCAFillModeForwards;
pathAnima.removedOnCompletion = NO;
[shapeLayer1 addAnimation:pathAnima forKey:nil];
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,245评论 4 61
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 173,388评论 25 708
  • 你是内心的想法 却是我要认知的做法 读不出来的是下一秒的时间 过去的是你对我的惩罚 情绪你在哪? 听不到你的回答 ...
    清欢的林夕阅读 230评论 2 1
  • 乡间的遗迹 鲁先圣 最近一年多的时间,常常在故乡的乡间里行走。过去,我曾经在故乡生活工作过多年,曾经自信对故乡...
    鲁先圣阅读 501评论 0 1
  • 爱的名义 你真的做到尊重了吗?你真的是在倾听吗?你真的是在全身心...
    禾雨分享阅读 400评论 2 3