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

Swift中UIBezierPath线性进度动画起始位置偏移问题排查

问题分析与解决方案

核心原因

最可能的问题出在布局时机不对和路径绘制的边界处理上:

  1. 你在创建进度条时传入的bookButton.frame.width是布局完成前的临时值,约束生效后视图实际宽度和myWidth不一致,导致动画进度的比例出现偏移。
  2. 当lineWidth等于视图高度时,线条会向路径两侧延伸lineWidth/2的长度,超出视图边界后被剪裁,视觉上造成起始位置偏移的错觉。

具体修复步骤

1. 确保在布局完成后初始化进度条

不要在viewDidLoad中获取控件frame(此时约束未生效,frame值不准确),改用viewDidLayoutSubviews:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    
    guard linearProgressBarButtonView == nil else {
        // 布局更新时重新调整路径
        linearProgressBarButtonView.myWidth = bookButton.bounds.width
        linearProgressBarButtonView.myHeight = bookButton.bounds.height
        linearProgressBarButtonView.createLinePath()
        return
    }
    
    linearProgressBarButtonView = LinearProgressBarButtonView(frame: .zero, width: bookButton.bounds.width, height: bookButton.bounds.height)
    view.addSubview(linearProgressBarButtonView)
    
    // 绑定和按钮一致的约束
    linearProgressBarButtonView.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([
        linearProgressBarButtonView.leadingAnchor.constraint(equalTo: bookButton.leadingAnchor),
        linearProgressBarButtonView.trailingAnchor.constraint(equalTo: bookButton.trailingAnchor),
        linearProgressBarButtonView.topAnchor.constraint(equalTo: bookButton.topAnchor),
        linearProgressBarButtonView.bottomAnchor.constraint(equalTo: bookButton.bottomAnchor)
    ])
    
    linearProgressBarButtonView.createLinePath()
}

2. 修复路径绘制,避免超出视图边界

修改createLinePath方法,调整路径的起点和终点,同时移除旧的layer避免重复叠加:

func createLinePath() {
    // 先移除旧图层,防止重复添加
    lineLayer.removeFromSuperlayer()
    progressLayer.removeFromSuperlayer()
    
    let halfHeight = myHeight / 2
    // 让路径的起点/终点向内偏移halfHeight,避免线条超出视图
    let startPoint = CGPoint(x: halfHeight, y: halfHeight)
    let endPoint = CGPoint(x: myWidth - halfHeight, y: halfHeight)
    
    let linePath = UIBezierPath()
    linePath.move(to: startPoint)
    linePath.addLine(to: endPoint)
    
    // 线条图层设置
    lineLayer.path = linePath.cgPath
    lineLayer.fillColor = UIColor.clear.cgColor
    lineLayer.lineCap = .square
    lineLayer.lineWidth = myHeight
    lineLayer.strokeEnd = 1.0
    lineLayer.strokeColor = colors.Blue.cgColor
    layer.addSublayer(lineLayer)
    
    // 进度图层设置
    progressLayer.path = linePath.cgPath
    progressLayer.fillColor = UIColor.clear.cgColor
    progressLayer.lineCap = .square
    progressLayer.lineWidth = myHeight
    progressLayer.strokeStart = 0 // 显式设置起始比例,消除歧义
    progressLayer.strokeEnd = 0
    progressLayer.strokeColor = colors.red.cgColor
    layer.addSublayer(progressLayer)
}

3. 修复动画的状态保留设置

当前动画结束后会重置回初始状态,修改动画参数确保结束状态保留:

func progressAnimation(duration: TimeInterval) {
    let linearProgressAnimation = CABasicAnimation(keyPath: "strokeEnd")
    linearProgressAnimation.duration = duration
    linearProgressAnimation.fromValue = 0.0 // 显式指定起始值
    linearProgressAnimation.toValue = 1.0
    linearProgressAnimation.fillMode = .forwards
    linearProgressAnimation.isRemovedOnCompletion = false // 动画结束后保留状态
    progressLayer.add(linearProgressAnimation, forKey: "progressAnim")
}

内容的提问来源于stack exchange,提问作者Jan L

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 17:55:42