基本思路其实很简单,就是通过贝塞尔曲线画出路径,然后
使用CAShapeLayer 渲染路径,然后通过strokeEnd 动画实现
路径的效果,这里注意,这个过程中过遇到过一个问题,就是
对号动画完成之后,整个对号不见了,后来经过仔细调查,发现,是自己初始化 checkLayer的时候,将strokeEnd属设置为0了,注意,虽然我们是通过"strokeEnd"设置的动画,但是我们进行动画之后,并不会真正的改变layer.strokeEnd属性的值,所以我们初始化对号layer的时候,还是要将strokeEnd设置为1
下面贴出所有代码
//
// ViewController.m
// LBProgressCircle
//
// Created by mac on 2024/5/31.
//
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) UIView *loadingView;
//进度圆环曲线
@property (nonatomic, strong) UIBezierPath *circlePath;
//整个圆环
@property (nonatomic, strong) CAShapeLayer *wholeCircleLayer;
//进度圆环layer
@property (nonatomic, strong) CAShapeLayer *progressLayer;
//对号圆环
@property (nonatomic, strong) CAShapeLayer *checkRoundLayer;
//对号layer
@property (nonatomic, strong) CAShapeLayer *checkLayer;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor lightGrayColor];
[self.view addSubview:self.loadingView];
[self.loadingView.layer addSublayer:self.wholeCircleLayer];
[self.loadingView.layer addSublayer:self.progressLayer];
[self.loadingView.layer addSublayer:self.checkLayer];
[self.loadingView.layer addSublayer:self.checkRoundLayer];
[self handle];
// Do any additional setup after loading the view.
}
- (void)handle
{
self.progressLayer.strokeEnd = 0;
__block CGFloat second = 0;
NSTimer *time = [NSTimer scheduledTimerWithTimeInterval:0.1 repeats:YES block:^(NSTimer * _Nonnull timer) {
if (second >= 1) {
return;
}
second += 0.1;
[self updateProgress:second];
}];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self handle];
}
- (void)updateProgress:(CGFloat)progress
{
self.progressLayer.strokeEnd = progress;
if (progress >= 1) {
[self showProgressCirce:NO];
[self.checkRoundLayer addAnimation:[self animation] forKey:@"strokeEnd"];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.checkLayer.hidden = NO;
[self.checkLayer addAnimation:[self animation] forKey:@"strokeEnd"];
});
} else {
self.checkLayer.hidden = YES;
[self showProgressCirce:YES];
}
}
- (void)showProgressCirce:(BOOL)showCircle
{
self.checkRoundLayer.hidden = showCircle;
self.wholeCircleLayer.hidden = !showCircle;
self.progressLayer.hidden = !showCircle;
}
#pragma mark - lazy load
- (UIView *)loadingView
{
if (!_loadingView) {
_loadingView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
_loadingView.backgroundColor = [UIColor blackColor];
}
return _loadingView;
}
- (UIBezierPath *)circlePath
{
if (!_circlePath) {
_circlePath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(27.5, 44) radius:19 startAngle:-M_PI_2 endAngle:-M_PI_2 + M_PI * 2 clockwise:YES];
_circlePath.lineCapStyle = kCGLineCapRound;
_circlePath.lineJoinStyle = kCGLineJoinRound;
}
return _circlePath;
}
- (CAShapeLayer *)progressLayer
{
if (!_progressLayer) {
_progressLayer = [[CAShapeLayer alloc] init];
_progressLayer.path = self.circlePath.CGPath;
_progressLayer.strokeStart = 0;
_progressLayer.strokeEnd = 0;
_progressLayer.strokeColor = [UIColor redColor].CGColor;
_progressLayer.fillColor = [UIColor clearColor].CGColor;
_progressLayer.lineWidth = 2;
}
return _progressLayer;
}
- (CAShapeLayer *)wholeCircleLayer
{
if (!_wholeCircleLayer) {
_wholeCircleLayer = [[CAShapeLayer alloc] init];
_wholeCircleLayer.path = self.circlePath.CGPath;
_wholeCircleLayer.strokeStart = 0;
_wholeCircleLayer.strokeEnd = 1;
_wholeCircleLayer.strokeColor = [[UIColor redColor] colorWithAlphaComponent:0].CGColor;
_wholeCircleLayer.fillColor = [UIColor clearColor].CGColor;
_wholeCircleLayer.lineWidth = 2;
}
return _wholeCircleLayer;
}
- (CAShapeLayer *)checkRoundLayer
{
if (!_checkRoundLayer) {
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(27.5, 44) radius:16 startAngle:- M_PI_2 endAngle:- M_PI_2 + M_PI * 2 clockwise:YES];
path.lineCapStyle = kCGLineCapRound;
path.lineJoinStyle = kCGLineJoinRound;
_checkRoundLayer = [[CAShapeLayer alloc] init];
_checkRoundLayer.path = path.CGPath;
_checkRoundLayer.strokeColor = [UIColor whiteColor].CGColor;
_checkRoundLayer.fillColor = [UIColor clearColor].CGColor;
_checkRoundLayer.hidden = YES;
_checkRoundLayer.lineWidth = 2;
}
return _checkRoundLayer;
}
- (CAShapeLayer *)checkLayer
{
if (!_checkLayer) {
UIBezierPath *path = [[UIBezierPath alloc] init];
path.lineCapStyle = kCGLineCapRound;
path.lineJoinStyle = kCGLineJoinRound;
[path moveToPoint:CGPointMake(19, 43)];
CGPoint pl = CGPointMake(25, 49);
[path addLineToPoint:pl];
CGPoint p2 = CGPointMake(36, 39);
[path addLineToPoint:p2];
_checkLayer = [[CAShapeLayer alloc] init];
_checkLayer.fillColor = [UIColor clearColor].CGColor;
//线条颜色
_checkLayer.strokeColor = [UIColor whiteColor].CGColor;
_checkLayer.lineWidth = 2;
_checkLayer.path = path.CGPath;
_checkLayer.hidden = YES;
}
return _checkLayer;
}
- (CABasicAnimation *)animation
{
CABasicAnimation *checkAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
checkAnimation.duration = 0.5;
checkAnimation.fromValue = @(0);
checkAnimation.toValue = @(1);
return checkAnimation;
}
@end