CALayer 介绍 无

CALayer Presentation nil

我正在尝试将 CABasicAnimation 用于自定义对象的计时功能(不是 UIView)。

我正在尝试实现来自 here 的@CIFilter 的回答,即使用动画化的 CALayer 表示层来评估计时功能。

我在 viewDidAppear 中完成所有操作,因此存在有效视图,但无论我做什么,Presentation 层始终为零。

请注意,我必须将动画添加到视图的层,而不是我添加到它的层才能使其完全动画。如果我取消注释下面注释掉的行,我可以看到动画有效(但仅在为根层设置动画时)。无论如何,Presentation 层为零。

我看了很多教程和 SO 答案,看来这应该行得通,所以我想我一定是在做一些愚蠢的事情。

我只是想使用 CoreAnimation 计时功能。我有 UICubicTimingParameters 工作,但似乎走 CA 路线提供了更多的功能,这很好。

import UIKit

class ViewController: UIViewController {

    override func viewDidAppear(_ animated: Bool) {
        
        super.viewDidAppear(animated)
    
        let newView = UIView(frame: view.frame)
        view.addSubview(newView)

        let evaluatorLayer = CALayer()
        evaluatorLayer.frame = CGRect(x: 0.0, y: 0.0, width: 100.0, height: 100.0)
        evaluatorLayer.borderWidth = 8.0
        evaluatorLayer.borderColor = UIColor.purple.cgColor
        evaluatorLayer.timeOffset = 0.3
        evaluatorLayer.isHidden = true
    //  evaluatorLayer.isHidden = false
    
        newView.layer.addSublayer(evaluatorLayer)
    
        let basicAnimation = CABasicAnimation(keyPath: "bounds.origin.x")
        basicAnimation.duration = 1.0
        basicAnimation.fromValue = 0.0
        basicAnimation.toValue = 100.0
        basicAnimation.fillMode = .forwards
        basicAnimation.isRemovedOnCompletion = false
        basicAnimation.speed = 0.0
    //  basicAnimation.speed = 0.1
    
        newView.layer.add(basicAnimation, forKey: "evaluate")

        if let presentationLayer = newView.layer.presentation() {
            let evaluatedValue = presentationLayer.bounds.origin.x
            print("evaluatedValue: \(evaluatedValue)")
        }
        else {
            print(evaluatorLayer.presentation())
        }
    
    }
}

不确定您的代码是否会按预期执行,但是...

我认为 .presentation()nil 的原因是你没有给 UIKit 应用动画的机会。

试试这个:

override func viewDidAppear(_ animated: Bool) {
    
    super.viewDidAppear(animated)
    
    let newView = UIView(frame: view.frame)
    view.addSubview(newView)
    
    let evaluatorLayer = CALayer()
    evaluatorLayer.frame = CGRect(x: 0.0, y: 0.0, width: 100.0, height: 100.0)
    evaluatorLayer.borderWidth = 8.0
    evaluatorLayer.borderColor = UIColor.purple.cgColor
    evaluatorLayer.timeOffset = 0.3
    evaluatorLayer.isHidden = true
    //  evaluatorLayer.isHidden = false
    
    newView.layer.addSublayer(evaluatorLayer)
    
    let basicAnimation = CABasicAnimation(keyPath: "bounds.origin.x")
    basicAnimation.duration = 1.0
    basicAnimation.fromValue = 0.0
    basicAnimation.toValue = 100.0
    basicAnimation.fillMode = .forwards
    basicAnimation.isRemovedOnCompletion = false
    basicAnimation.speed = 0.0
    //  basicAnimation.speed = 0.1
    
    newView.layer.add(basicAnimation, forKey: "evaluate")

    DispatchQueue.main.async {
        
        if let presentationLayer = newView.layer.presentation() {
            let evaluatedValue = presentationLayer.bounds.origin.x
            print("async evaluatedValue: \(evaluatedValue)")
        }
        else {
            print("async", evaluatorLayer.presentation())
        }
        
    }

    if let presentationLayer = newView.layer.presentation() {
        let evaluatedValue = presentationLayer.bounds.origin.x
        print("immediate evaluatedValue: \(evaluatedValue)")
    }
    else {
        print("immediate", evaluatorLayer.presentation())
    }
    
}

我的调试输出是:

immediate nil
async evaluatedValue: 0.0

编辑

我仍然不确定你的目标是什么,但试一试...

override func viewDidAppear(_ animated: Bool) {
    
    super.viewDidAppear(animated)
    
    let newView = UIView(frame: view.frame)
    view.addSubview(newView)
    
    let evaluatorLayer = CALayer()
    evaluatorLayer.frame = CGRect(x: 0.0, y: 0.0, width: 100.0, height: 100.0)
    evaluatorLayer.borderWidth = 8.0
    evaluatorLayer.borderColor = UIColor.purple.cgColor
    evaluatorLayer.isHidden = true
    //evaluatorLayer.isHidden = false
    
    newView.layer.addSublayer(evaluatorLayer)
    
    let basicAnimation = CABasicAnimation(keyPath: "bounds.origin.x")
    basicAnimation.duration = 1.0
    basicAnimation.fromValue = 0.0
    basicAnimation.toValue = 100.0
    basicAnimation.fillMode = .forwards
    basicAnimation.isRemovedOnCompletion = false
    basicAnimation.speed = 0.0
    //basicAnimation.speed = 1.0

    // set timeOffset on the animation, not on the layer itself
    basicAnimation.timeOffset = 0.3

    // add animation to evaluatorLayer
    evaluatorLayer.add(basicAnimation, forKey: "evaluate")
    
    DispatchQueue.main.async {
        
        // get presentation layer of evaluatorLayer
        if let presentationLayer = evaluatorLayer.presentation() {
            let evaluatedValue = presentationLayer.bounds.origin.x
            print("async evaluatedValue: \(evaluatedValue)")
        }
        else {
            print("async", evaluatorLayer.presentation())
        }
        
    }

}

在此示例中,我们将 .timeOffset 应用于动画,而不是图层。而且,我们将动画添加到 evaluatorLayer,而不是 newView.layer

输出(用于我的快速测试):

async evaluatedValue: 30.000001192092896