【问题标题】:Linear CABasicAnimation isn't executed linearly in Container View线性 CABasicAnimation 不在容器视图中线性执行
【发布时间】:2018-04-06 16:26:42
【问题描述】:

即使将timingFunction显式设置为linear,动画也不会线性执行。

我使用下面的代码来初始化动画。

再往下是整个类的实现以及ViewController在InterfaceBuilder中是如何设置的

private func timeLayerAnimation() {
    let animation = CABasicAnimation()
    animation.keyPath = "strokeEnd"
    animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
    animation.duration = 240.0
    animation.toValue = 0
    animation.fillMode = kCAFillModeForwards
    animation.isRemovedOnCompletion = false

    timeLayer.add(animation, forKey: nil)
}

视图如下所示。

动画总时长为 240 秒。

但在 30 秒后。已经只有 75 % 的圆圈仍然可见。

停止时间如下:

75 % (1.5 π):    30  sec. (∆ 30 sec.)

50 % (1 π):      70  sec. (∆ 40 sec.)

25 % (0.5 π):    120 sec. (∆ 50 sec.)

// 13 % (0.25 π):   155 sec.

0  % (0 π):      240 sec. (∆ 120 sec.)

更新

我发现当负责动画的 ViewController 位于容器视图中时会出现问题。

我的猜测是它可能与默认的 UIViewAnimationCurve 有关,但我不确定,我不知道如何开始测试:(

Container View 的所有侧面都固定在安全区域。 MainVC 的实现是空的,EmbeddedVC 如下所示:

import UIKit


class EmbeddedVC: UIViewController {
    // MARK: - Properties

    let timeLayer = CAShapeLayer()

    // MARK: - View Lifecycle

    override func viewDidLayoutSubviews() {
        setupTimerLayout()
    }

}

// MARK: - Timer Layout Setup
extension EmbeddedVC {

    func setupTimerLayout() {
        let circularPath = UIBezierPath.init(arcCenter: .zero,
                                             radius: view.frame.width * 0.36,
                                             startAngle: 0,
                                             endAngle: 2 * CGFloat.pi,
                                             clockwise: true)

        // Configure time layer
        timeLayer.path = circularPath.cgPath
        timeLayer.fillColor = UIColor.clear.cgColor
        timeLayer.lineCap = kCALineCapRound
        timeLayer.strokeColor = UIColor.red.cgColor
        timeLayer.strokeEnd = 1
        timeLayer.lineWidth = 10
        timeLayer.position = view.center

        view.layer.addSublayer(timeLayer)
        animateTimeLayer()
    }


    private func animateTimeLayer() {
        let animation = CABasicAnimation()
        animation.keyPath = "strokeEnd"
        animation.duration = 240.0
        animation.toValue = 0
        animation.fillMode = kCAFillModeForwards
        animation.isRemovedOnCompletion = false

        timeLayer.add(animation, forKey: nil)
    }

}

【问题讨论】:

  • 感谢您的回答 :) 我认为这可能与典型的初学者错误有关。我会尝试找出错误/做一个 MCVE。
  • @Rob 我更新了答案并添加了一种 MCVE。正如更新的答案中所述,当 ViewController 嵌入到容器视图中时会出现问题。

标签: swift animation cabasicanimation


【解决方案1】:

问题是viewDidLayoutSubviews 可以被多次调用,而您没有指定fromValue,所以这两个动画相互干扰。您可以通过以下任意组合来解决此问题:

  1. 添加检查以查看您是否已经开始动画:

    var hasStarted = false
    
    private func startAnimation() {            
        guard !hasStarted else { return }
    
        hasStarted = true
    
        ...
    }
    

    (注意,我建议您在覆盖这些方法时始终调用super。)

  2. 在开始另一个动画之前删除动画。

  3. 在您的动画中指定 fromValue

    private func startAnimation() {
        let animation = CABasicAnimation(keyPath: "strokeEnd")
        animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
        animation.duration = ...
        animation.fromValue = 1  // by setting this, it won't get confused if you start the animation again
        animation.toValue = 0
        ...
    
        shapeLayer.add(animation, forKey: nil)
    }
    
  4. 推迟到viewDidAppear:

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
    
        // start your animation here
    }
    

就个人而言,我会同时做 #3 和 #4,但你可以做任何最适合你的事情。

【讨论】:

    【解决方案2】:

    @Rob 非常感谢你的回答,这真的让我发疯了...... :)

    现在一切都很完美。我只想为那些使用扩展并且不想在基类上使用局部变量或无法访问基类的人添加一些东西。

    private func animateTimeLayer() {
        let animationKey = "animateTimeLayerKey"
    
        guard nil == timeLayer.animation(forKey: animationKey) else { return }
    
        let animation = CABasicAnimation()
    
        ...
    
        timeLayer.add(animation, forKey: animationKey)
    }
    

    【讨论】:

      猜你喜欢
      • 2020-04-22
      • 2021-10-16
      • 2020-01-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-14
      • 1970-01-01
      • 2016-03-28
      相关资源
      最近更新 更多