前言
实现了一款时下比较流行的环状进度动图,以下是源码解析
使用 Core Graphics 和 定时器 实现环形进度动图
核心源码
# 使用 [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];
}
}
小球图片的坐标计算参照以下这张图
#设置进度 属性的 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>
核心源码
# 橘红色的背景
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];