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

CAAnimationGroup嵌套两个CABasicAnimation的动画异常问题

问题描述

想要实现标签栏的线条动画效果,当前代码已接近预期,但动画组完成后,两个指定点之间会保留一条实线,而非在第二个动画结束后结束图层状态。

当前代码如下:

let start = fromTab.convert(fromLineView.center, to: backgroundView)
let end = toTab.convert(toLineView.center, to: backgroundView)

let path = UIBezierPath()
path.move(to: start)
path.addLine(to: end)

let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayer.strokeColor = ColorConstants.green.cgColor
shapeLayer.lineWidth = 4
shapeLayer.lineCap = .round

backgroundView.layer.addSublayer(shapeLayer)

let startAnimation = CABasicAnimation(keyPath: "strokeEnd")
startAnimation.duration = 1
startAnimation.fromValue = 0
startAnimation.toValue = 1

let endAnimation = CABasicAnimation(keyPath: "strokeStart")
endAnimation.beginTime = startAnimation.duration
endAnimation.duration = 1
endAnimation.fromValue = 0
endAnimation.toValue = 1

let animation = CAAnimationGroup()
animation.animations = [startAnimation, endAnimation]
animation.duration = startAnimation.duration + endAnimation.duration
shapeLayer.add(animation, forKey: "lineAnimation")

当前效果:动画结束后两点间仍保留实线,不符合预期。

解决方案

问题核心在于Core Animation默认仅驱动视觉动画,不会修改图层的真实属性,动画结束后图层会自动回到初始状态(strokeStart=0、strokeEnd=1),因此会显示完整线条。可通过以下步骤修复:

  1. 设置图层初始状态为动画结束后的状态
    在添加动画前,将shapeLayer的strokeStart和strokeEnd都设为1,确保动画结束后图层保持无线条的状态。

  2. 调整动画的状态过渡逻辑
    保持原有动画的fromValue和toValue逻辑,同时通过fillMode和removedOnCompletion确保动画过程中状态正确过渡。

  3. 可选:动画结束后移除图层
    若希望线条完全消失后移除图层,可添加动画完成回调,手动移除shapeLayer。

修改后的完整代码:

let start = fromTab.convert(fromLineView.center, to: backgroundView)
let end = toTab.convert(toLineView.center, to: backgroundView)

let path = UIBezierPath()
path.move(to: start)
path.addLine(to: end)

let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayer.strokeColor = ColorConstants.green.cgColor
shapeLayer.lineWidth = 4
shapeLayer.lineCap = .round
// 设置图层初始状态为动画结束后的无线条状态
shapeLayer.strokeStart = 1
shapeLayer.strokeEnd = 1

backgroundView.layer.addSublayer(shapeLayer)

let startAnimation = CABasicAnimation(keyPath: "strokeEnd")
startAnimation.duration = 1
startAnimation.fromValue = 0
startAnimation.toValue = 1
startAnimation.fillMode = .forwards
startAnimation.removedOnCompletion = false

let endAnimation = CABasicAnimation(keyPath: "strokeStart")
endAnimation.beginTime = startAnimation.duration
endAnimation.duration = 1
endAnimation.fromValue = 0
endAnimation.toValue = 1
endAnimation.fillMode = .forwards
endAnimation.removedOnCompletion = false

let animation = CAAnimationGroup()
animation.animations = [startAnimation, endAnimation]
animation.duration = startAnimation.duration + endAnimation.duration
animation.fillMode = .forwards
animation.removedOnCompletion = false

// 可选:动画完成后移除线条图层
CATransaction.setCompletionBlock {
    shapeLayer.removeFromSuperlayer()
}
shapeLayer.add(animation, forKey: "lineAnimation")
CATransaction.commit()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 14:55:15