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

CABasicAnimation描边进度异常:字符计数进度条仅达70%求助

字符计数圆环进度无法满值的问题排查与修复

问题描述

我实现了一个类似Twitter的自定义字符计数进度圆环,通过CABasicAnimation的strokeEnd属性实现描边动画。但存在计算逻辑问题:当字符数达到上限280时,描边进度仅约70%,无法达到预期的100%。

实现代码

CharacterCountView 类

class CharacterCountView: UIView {

private let progressLayer = CAShapeLayer()

override init(frame: CGRect) {
    super.init(frame: frame)
    setUpView()
}

required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

@objc func animateToValue(value: Double) {
    let animation = CABasicAnimation(keyPath: "strokeEnd")
    animation.toValue = value
    animation.duration = 0.5
    animation.fillMode = .forwards
    animation.isRemovedOnCompletion = false
    progressLayer.add(animation, forKey: "strokeAnimation")
    progressLayer.strokeEnd = value
    
    if value > 1 {
        print("100% REACHED")
    }
}
}

extension CharacterCountView {

fileprivate func setUpView() {
    addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(animateToValue)))
    let trackLayer = CAShapeLayer()
    trackLayer.frame = bounds
    let path = UIBezierPath(arcCenter: trackLayer.position, radius: 8, startAngle: -CGFloat.pi / 2, endAngle: 2 * CGFloat.pi, clockwise: true)
    trackLayer.path = path.cgPath
    trackLayer.strokeColor = UIColor.systemGray2.cgColor
    trackLayer.lineWidth = 1.5
    trackLayer.fillColor = Color.background.cgColor
    trackLayer.lineCap = .round
    layer.addSublayer(trackLayer)
    progressLayer.frame = bounds
    progressLayer.path = path.cgPath
    progressLayer.strokeColor = Color.accent.cgColor
    progressLayer.lineWidth = 1.5
    progressLayer.fillColor = UIColor.clear.cgColor
    progressLayer.lineCap = .round
    progressLayer.strokeEnd = 0
    layer.addSublayer(progressLayer)
}
}

文本变化处理方法

func textDidChange(to value: String) {
        if value.isEmpty || value.count > 280 {
            postBarButtonItem.isEnabled = false
        } else {
            postBarButtonItem.isEnabled = true
        }
    
    let characterCount = Double(value.count)
    let percentage = characterCount / 280
    print(percentage)
    createPostInputAccessoryView.characterCountView.animateToValue(value: percentage)
}

问题原因

核心问题出在圆环路径的角度范围设置错误:

  • 当前代码创建的贝塞尔路径,startAngle为-CGFloat.pi/2(对应12点钟方向),endAngle为2*CGFloat.pi(对应3点钟方向),顺时针绘制的总角度跨度是2.5 * CGFloat.pi(一圈加四分之一圈)。
  • strokeEnd的取值范围0-1对应路径的总长度比例,所以当传入1(280字符时的percentage=1),实际只走完了这个超长路径的全部,但其中只有2π / 2.5π = 0.8(即80%)的部分是完整圆环的范围,剩余四分之一圈超出了圆环可视区域,导致视觉上进度仅约70%-80%。

修复方案

1. 修正圆环路径的角度范围

将路径的endAngle改为3*CGFloat.pi/2,这样从-π/2到3π/2顺时针绘制,总角度跨度正好是2π(完整一圈),此时strokeEnd=1就对应完整的圆环描边:

// 修改 setUpView 中的路径创建代码
let path = UIBezierPath(arcCenter: trackLayer.position, radius: 8, startAngle: -CGFloat.pi / 2, endAngle: 3 * CGFloat.pi / 2, clockwise: true)

2. 修复手势响应方法的参数错误

当前手势绑定的animateToValue方法参数不匹配,点击时会崩溃,修正为:

@objc func animateToValue(_ gesture: UITapGestureRecognizer) {
    // 示例:点击时测试满进度
    let testValue = 1.0
    let animation = CABasicAnimation(keyPath: "strokeEnd")
    animation.toValue = testValue
    animation.duration = 0.5
    animation.fillMode = .forwards
    animation.isRemovedOnCompletion = false
    progressLayer.add(animation, forKey: "strokeAnimation")
    progressLayer.strokeEnd = testValue
    
    if testValue > 1 {
        print("100% REACHED")
    }
}

3. 优化字符数超过上限的处理

当字符数超过280时,限制进度比例不超过1,并可添加样式提示:

func textDidChange(to value: String) {
    postBarButtonItem.isEnabled = !value.isEmpty && value.count <= 280

    let characterCount = Double(value.count)
    var percentage = characterCount / 280
    // 限制最大比例为1,避免进度超出圆环
    percentage = min(percentage, 1.0)
    print(percentage)
    createPostInputAccessoryView.characterCountView.animateToValue(value: percentage)
    
    // 可选:超过字符数时将圆环改为红色
    if value.count > 280 {
        createPostInputAccessoryView.characterCountView.progressLayer.strokeColor = UIColor.systemRed.cgColor
    } else {
        createPostInputAccessoryView.characterCountView.progressLayer.strokeColor = Color.accent.cgColor
    }
}

内容的提问来源于stack exchange,提问作者David Henry

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 05:35:22