You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用BezierPath实现UIButton外围无限循环旋转加载动画?

UIButton 外围循环加载动画实现方案

核心思路

放弃多图层+strokeStart/strokeEnd的组合方案,改用固定虚线模式+路径偏移动画的单图层方案,仅需1个CAShapeLayer+1个基础动画即可实现需求,完美匹配「分段长度固定、整体匀速循环旋转」的效果要求。

具体实现步骤

  • 步骤1:生成按钮外围闭合贝塞尔路径
    直接沿按钮尺寸生成对应圆角的闭合路径即可,以圆角按钮为例:
    // Objective-C 示例
    UIBezierPath *borderPath = [UIBezierPath bezierPathWithRoundedRect:button.bounds cornerRadius:button.layer.cornerRadius];
    
    // Swift 示例
    let borderPath = UIBezierPath(roundedRect: button.bounds, cornerRadius: button.layer.cornerRadius)
    
  • 步骤2:创建并配置基础CAShapeLayer属性
    仅需单个图层即可,无需多图层叠加:
    CAShapeLayer *loadingLayer = [CAShapeLayer layer];
    loadingLayer.frame = button.bounds;
    loadingLayer.path = borderPath.CGPath;
    loadingLayer.fillColor = UIColor.clearColor.CGColor;
    loadingLayer.strokeColor = UIColor.systemBlueColor.CGColor; // 替换为设计图对应线条颜色
    loadingLayer.lineWidth = 2; // 替换为设计图对应线条粗细
    loadingLayer.lineCap = kCALineCapRound; // 圆角端点,实现圆点效果的核心配置
    
  • 步骤3:配置固定长度的分段虚线模式
    先计算按钮外围路径的总周长,再按需求设置lineDashPattern的分段参数:
    // 计算闭合路径总周长
    CGFloat btnWidth = button.bounds.size.width;
    CGFloat btnHeight = button.bounds.size.height;
    CGFloat cornerRadius = button.layer.cornerRadius;
    CGFloat totalPerimeter = 2*(btnWidth + btnHeight) - 4*cornerRadius + 2*M_PI*cornerRadius;
    
    // 按需求配置三段长度:半长线段 + 圆点 + 空白
    CGFloat lineSegmentLength = btnWidth / 2; // 线段长度为按钮宽度的一半
    CGFloat dotLength = loadingLayer.lineWidth; // 圆点长度等于线宽,配合圆角端点就是正圆点
    CGFloat emptyLength = totalPerimeter - lineSegmentLength - dotLength;
    
    // 虚线规则:[实线段长度, 间隔长度, 圆点实线段长度, 空白段长度]
    loadingLayer.lineDashPattern = @[@(lineSegmentLength), @(dotLength), @(dotLength), @(emptyLength)];
    

    lineDashPattern的规则是按数组顺序交替取数值作为实线、空白的长度,上述配置刚好对应「长线段→短间隔→圆点→长空白」的循环结构,所有分段相对位置完全固定。

  • 步骤4:添加无限循环的匀速偏移动画
    通过修改lineDashPhase属性实现整体路径滚动效果:
    CABasicAnimation *dashAnimation = [CABasicAnimation animationWithKeyPath:@"lineDashPhase"];
    dashAnimation.fromValue = @(0);
    dashAnimation.toValue = @(-totalPerimeter);
    dashAnimation.duration = 2; // 旋转一圈的时长,可按需调整
    dashAnimation.repeatCount = INFINITY;
    dashAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
    [loadingLayer addAnimation:dashAnimation forKey:@"buttonLoadingAnimation"];
    
  • 步骤5:将loadingLayer添加到按钮的图层上即可,按钮尺寸发生变化时同步更新path和lineDashPattern参数即可适配。

常见效果异常排查

如果最终效果和预期不符,优先检查三个参数:

  • 总周长计算是否准确,周长错误会导致空白段长度不对,出现多段线条的问题
  • lineCap是否设置为圆角,否则圆点会变成方形短线条
  • 动画timingFunction是否为线性,否则会出现旋转速度忽快忽慢的问题

内容的提问来源于stack exchange,提问作者Viktor Todorov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 19:54:05