封装了一个iOS对号成功动画

基本思路其实很简单,就是通过贝塞尔曲线画出路径,然后

使用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
相关推荐
HarderCoder7 小时前
Swift 中的基本运算符:从加减乘除到逻辑与或非
ios·swift
HarderCoder7 小时前
Swift 中“特性开关”实战笔记——用编译条件+EnvironmentValues优雅管理Debug/TestFlight/AppStore三环境
ios·swift
HarderCoder7 小时前
Swift 并发任务中到底该不该用 `[weak self]`?—— 从原理到实战一次讲透
ios·swift
FeliksLv8 小时前
iOS 集成mars xlog
ios
2501_9151063210 小时前
CDN 可以实现 HTTPS 吗?实战要点、部署模式与真机验证流程
网络协议·http·ios·小程序·https·uni-app·iphone
大熊猫侯佩2 天前
在肖申克监狱玩转 iOS 26:安迪的 Liquid Glass 复仇计划
ios·swiftui·swift
非专业程序员2 天前
逆向分析CoreText中的字体级联/Font Fallback机制
前端·ios
库奇噜啦呼2 天前
【iOS】简单的四则运算
macos·ios·cocoa
white-persist2 天前
【burp手机真机抓包】Burp Suite 在真机(Android and IOS)抓包手机APP + 微信小程序详细教程
android·前端·ios·智能手机·微信小程序·小程序·原型模式
恋猫de小郭3 天前
Fluttercon EU 2025 :Let‘s go far with Flutter
android·开发语言·flutter·ios·golang