【问题标题】:CALayer Presentation nilCALayer 演示文稿 无
【发布时间】:2021-05-08 19:07:18
【问题描述】:

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

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

我在viewDidAppear 中做这一切,所以存在一个有效的视图,但无论我做什么,Presentation 层总是为零。

请注意,我必须将动画添加到视图的图层,而不是我添加到其中的图层才能让它产生动画。如果我取消注释下面注释掉的行,我可以看到动画有效(但仅在为根层设置动画时)。不管怎样,Presentation 层是 nil。

我已经查看了几十个教程和 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())
        }
    
    }
}

【问题讨论】:

    标签: uiview core-animation calayer cabasicanimation


    【解决方案1】:

    不确定您的代码是否会达到您的预期,但是...

    我认为.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
    

    【讨论】:

    • 我期待它能够工作,因为这就是我链接到的答案所说的并且已经被赞成。但是,是的,我现在得到了一个表示层,但evaluatedValue 是 0.0,即使我设置了 timeOffset。所以还是有问题,但你确实回答了为什么presentation is nil的问题,所以我接受了。
    • @JeshuaLacock - 请参阅我的答案的编辑
    • 谢谢。这实际上奏效了 - 一次。但是每次都是0。嗯,无论如何都必须使用 .async 来做我想要使用它的东西,这有点时髦,所以我想我会使用我现在拥有的东西。该死的耻辱  没有公开 CATimer 函数以用于我们自己的对象。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-13
    • 1970-01-01
    • 1970-01-01
    • 2011-03-02
    • 1970-01-01
    相关资源
    最近更新 更多