swift - 线性 CABasicAnimation 在容器 View 中不是线性执行的

标签 swift animation cabasicanimation

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

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

再往下就是整个类的实现以及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)
}

View 如下所示。 enter image description here

总动画时长为 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 位于容器 View 中时会出现问题。

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

enter image description here

容器 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)
    }

}

最佳答案

问题是 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 我都会做,但您可以做任何最适合您的事情。

关于swift - 线性 CABasicAnimation 在容器 View 中不是线性执行的,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49697216/

相关文章:

iOS - 同步保存到 UserDefaults 吗?

ios - 解析 PFQueryTableViewController loadNextPage() 重新加载 tableView 问题

objective-c - 如何通过按住 UIView 来降低旋转图像的速度?

ios - CABasicAnimation从当前图层位置开始

ios - 本地化字符串仅通过 Interface Builder 应用翻译

ios - 如何为 Mapview iOS 中添加的 MKOverlayRenderer 设置动画?

css - 现代谷歌加载器不能在 IE 中工作

java - 在小程序上显示旋转的风扇

ios - SpriteKit 动画 - 保持 Sprite 固定

ios - 在 CABasicAnimation 中使用 #keyPath 时,类型 'CGPoint' 没有成员 'x'