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

CALayer宽度动画异常:从Started到Awaiting...标签的填充动画问题

Hey there! Let's figure out why your fill animation isn't behaving as expected. Looking at your code and the desired effect, there are a couple of key issues we can fix to get that smooth fill from "started" to "awaiting...".

Key Issues in Your Current Implementation

  • Wrong Layer Parent: You're adding the animation layer to ivStarted.layer, which means the layer is constrained within the bounds of the "started" label. To cover the entire area from "started" to "awaiting...", the layer should be added to the parent container viewStartedAwaited.layer instead.
  • Coordinate System Mismatch: The frame you set for the layer uses viewStartedAwaited's origin, but since you were adding it to ivStarted.layer, this puts the layer in the wrong position relative to its parent.
  • Redundant Animation Group: You're using a CAAnimationGroup with only one animation—this isn't necessary, and simplifies things to use just CABasicAnimation.
  • Conflicting Animation Values: Setting fromValue, byValue, and toValue together can cause unexpected behavior; stick to fromValue and toValue for clear control.

Fixed Implementation

func startProgressAnimation() {
    self.ivStarted.isHighlighted = true
    
    // Create the fill layer and add it to the container view's layer
    let fillLayer = CALayer()
    fillLayer.backgroundColor = #colorLiteral(red: 0.4980392157, green: 0.8352941176, blue: 0.1921568627, alpha: 1)
    fillLayer.masksToBounds = true
    
    // Set initial frame: position at the top-left of the container, height matching the container, width 0
    fillLayer.frame = CGRect(
        x: 0,
        y: 0,
        width: 0,
        height: self.viewStartedAwaited.bounds.height
    )
    self.viewStartedAwaited.layer.addSublayer(fillLayer)
    
    // Create the width animation
    let widthAnimation = CABasicAnimation(keyPath: "bounds.size.width")
    widthAnimation.fromValue = 0.0
    widthAnimation.toValue = self.viewStartedAwaited.bounds.width
    widthAnimation.duration = 5.0
    widthAnimation.isRemovedOnCompletion = false
    widthAnimation.fillMode = .both
    
    // Update the layer's actual width to match the animation end state (prevents snap-back)
    fillLayer.bounds.size.width = self.viewStartedAwaited.bounds.width
    
    CATransaction.begin()
    CATransaction.setCompletionBlock { [weak self] in
        self?.ivAwaited.isHighlighted = true
    }
    fillLayer.add(widthAnimation, forKey: "widthFillAnimation")
    CATransaction.commit()
}

What Changed & Why

  1. Layer Parent Updated: The fill layer is now added to viewStartedAwaited.layer, so it can span the entire width of the container holding both labels.
  2. Correct Initial Frame: The layer starts at (0, 0) relative to the container, with height matching the container—this ensures it covers the full background area we need to fill.
  3. Simplified Animation: We removed the unnecessary animation group and focused on a single CABasicAnimation for width.
  4. Persistent Layer State: We explicitly set fillLayer.bounds.size.width to the final value before adding the animation. This prevents the layer from snapping back to width 0 once the animation finishes, even with isRemovedOnCompletion = false.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:44:19