CAKeyframeAnimationGroup位置

时间:2018-01-08 07:03:58

标签: ios swift animation caanimation

我有一堆添加到CALayer的动画。我需要知道按钮在动画时与其进行交互的位置。但框架并没有改变。 如何查找/计算值?这是代码:

    let positionY = CAKeyframeAnimation(keyPath: "position.y")
    positionY.values = valuesY
    positionY.keyTimes = keyTimes
    positionY.duration = totalTime
    positionY.isAdditive = true
    positionY.isRemovedOnCompletion = false
    // X Position Animation
    let positionX = CAKeyframeAnimation(keyPath: "position.x")
    positionX.values = valuesX
    positionX.keyTimes = keyTimes
    positionX.duration = 5
    positionX.isAdditive = true
    positionX.isRemovedOnCompletion = false
    // Rotation Animation
    let rotation = CAKeyframeAnimation(keyPath: "transform.rotation.z")
    rotation.values = valuesRotation
    rotation.keyTimes = keyTimes
    rotation.duration = totalTime
    rotation.isAdditive = true
    rotation.isRemovedOnCompletion = false
    // Grouping the animations
    let group = CAAnimationGroup()
    group.animations = [positionX, positionY, rotation]
    group.duration = totalTime
    group.fillMode = kCAFillModeForwards
    group.delegate = self
    group.setValue(button, forKey: GiftGameVC.kAnimationKeyPath)
    group.isRemovedOnCompletion = false
    //
    CATransaction.begin()
    CATransaction.setDisableActions(false)
    CATransaction.setCompletionBlock {
        print("Done")
    }
    button.layer.add(group, forKey: GiftGameVC.kAnimationKeyPath)

    CATransaction.commit()

2 个答案:

答案 0 :(得分:0)

您需要检查按钮的presentationLayer,因为您基本上正在观看电影,并且真实的图层位置永远不会像您找到的那样发生变化。一个例子就像下面这样。此外,对于图层动画,视图的框架将永远不会更改,只有图层位置和图层框架。 UIView动画可以更新视图的框架。

import UIKit

class ViewController: UIViewController {

    var timer : Timer?
    var animationView = UIView()
    var currentPositionLabel = UILabel()

    override func viewDidLoad() {
        super.viewDidLoad()
        animationView = UIView(frame: CGRect(x: 0, y: 0, width: 30, height: 30))
        animationView.layer.cornerRadius = 15
        animationView.backgroundColor = UIColor.blue
        self.view.addSubview(animationView)

        let button = UIButton(frame: CGRect(x: 20, y:self.view.bounds.height - 60, width: self.view.bounds.width - 40, height: 40))
        button.addTarget(self, action: #selector(startAnimation), for: .touchUpInside)
        button.backgroundColor = UIColor.green
        button.setTitle("Restart Animation", for: .normal)
        self.view.addSubview(button)

        currentPositionLabel = UILabel(frame: CGRect(x: 20, y:button.frame.minY - 50, width: self.view.bounds.width - 40, height: 40))
        currentPositionLabel.textColor = UIColor.gray
        currentPositionLabel.adjustsFontSizeToFitWidth = true
        currentPositionLabel.minimumScaleFactor = 0.01
        self.view.addSubview(currentPositionLabel)

        currentPositionLabel.text = "Current Position \(animationView.layer.position)"
    }

    @objc func startAnimation(){

        //prepare to watch a movie/animation while your layer never really moves
        CATransaction.begin()
        let animation = CABasicAnimation(keyPath: "position")
        animation.duration = 20.0
        animation.toValue = NSValue(cgPoint: CGPoint(x: self.view.bounds.width - 35, y: self.view.bounds.height - 120))
        CATransaction.setCompletionBlock {
            self.timer?.invalidate()
            self.timer = nil
            print("the real position never changed \(self.animationView.layer.position)")
            self.animationView.layer.removeAllAnimations()
            self.currentPositionLabel.text = "Current Position \(self.animationView.layer.position)"
        }
        animationView.layer.add(animation, forKey: "animationViewPosition")
        //if you really want to update the real position of the layer i do it here
        //self.animationView.layer.position = CGPoint(x: self.view.bounds.width - 35, y: self.view.bounds.height - 120)
        //this would mean when the animation is removed that the layer is there but still would not give you the current value
        //of the layer position
        CATransaction.commit()

        if let _ = timer{
            //do nothing we are good to go
        }else{
            //start a new timer
            timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(timerUpdateAnimationPosition), userInfo: nil, repeats: true)
        }
    }

    @objc func timerUpdateAnimationPosition(){
        //get presentation layer
        if let presLayer = self.animationView.layer.presentation(){
            if let animationPosition = presLayer.value(forKeyPath: "position") as? CGPoint{
                self.currentPositionLabel.text = "Animation Position : \(animationPosition)"
            }

        }
    }
}

你可以在函数timerUpdateAnimationPosition中看到我检查了presentationLayer的动画位置。对于动画位置的互动,请参阅此answer。但如果你正在寻找它,那就意味着上面的答案也是正确的。

答案 1 :(得分:0)

你已经注意到了;为该键路径添加动画时,属性的值不会更改。如果您想知道屏幕当前显示的图层状态,您可以访问图层的presentationLayer

  

此方法返回的图层对象提供了当前在屏幕上显示的图层的近似值。当动画正在进行时,您可以检索此对象并使用它来获取这些动画的当前值。

在您的情况下,您可以使用button.layer.presentationLayer.position获取按钮动画位置。

相关:由于您在完成动画并使用前向填充模式后停止移动动画,因此您将在“模型图层”和“表示层”之间保持这种差异。换一种说法;你让它看起来像这些属性已经改变,但实际上它们没有。这通常被认为是一种不好的做法,可能导致奇怪和无意的行为;例如,一个按钮现在可以在屏幕上显示,但同时可以在动画播放前的位置进行点播。

要避免这些类型的问题,最好在添加动画之前更新属性,使其与动画的最终值匹配。这样,“模型层”和“表示层”之间的差异仅存在于动画期间。

在您的代码中,您将删除所有.isRemovedOnCompletion = false行和.fillMode = kCAFillModeForwards行,而是将按钮图层的positiontransform设置为其最终值将动画组添加到图层。