Swift中UIBezierPath线性进度动画起始位置偏移问题排查
问题分析与解决方案
核心原因
最可能的问题出在布局时机不对和路径绘制的边界处理上:
- 你在创建进度条时传入的
bookButton.frame.width是布局完成前的临时值,约束生效后视图实际宽度和myWidth不一致,导致动画进度的比例出现偏移。 - 当
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
相关产品推荐
相关产品推荐

