繁体   English   中英

CALayer动画beginTime

[英]CALayer animation beginTime

我正在为iOS开发一个关键帧动画编辑器,该编辑器允许用户创建简单的动画并查看它们,并根据时间线进行绘制。 用户可以拖动时间线以更改当前时间。

这意味着我需要能够在用户指定的时间开始动画。

尽管可以实现此功能,但每次将动画重新添加到图层时,我也会遇到烦人的故障。 该故障会导致动画的第一帧在CoreAnimation遵守开始时间之前快速闪烁。 通过在刷新前将layer.alpha设置为0,在刷新后将layer.alpha设置为1,可以稍微减轻这种效果,但是这仍然会导致讨厌的单帧闪光!

我已经重新创建了一个示例视图控制器,该示例控制器演示了“更改正在运行的动画的时间”所需的代码,但是由于该项目是如此简单,因此您看不到冲洗的负面影响: https://gist.github .com / chrisbirch / 5cafca50804cf9d778ccd0fdc9e68d56

该代码的基本思想如下:

每次用户更改当前时间时,我都会重新启动动画,并使用CALayer计时属性进行调整(addStoppedAnimation:line 215):

ani = createGroup()

animatableLayer.speed = 0

animatableLayer.add(ani, forKey: "an animation key")
let time = CFTimeInterval(slider.value)
animatableLayer.timeOffset = 0
animatableLayer.beginTime = animatableLayer.superlayer!.convertTime(CACurrentMediaTime(), from: nil) - time

CATransaction.flush()
animatableLayer.timeOffset = time// offset
print("Time changed \(time)")

故障是由于我必须在设置timeOffset之前立即调用CATransaction.flush引起的。 未能调用此刷新会导致开始时间被忽略。

我觉得我已经搜寻了整个互联网,以寻求解决此问题的方法,但是Im,我认为我陷入了困境。

我的问题是这样的:

任何人都可以阐明为什么我需要调用CATransaction.flush才能使我设置的beginTime值生效吗? 查看苹果代码,我从来没有见过为此目的使用冲洗功能,所以也许我遇到了明显的错误!

提前谢谢了

克里斯

使用您的要旨中的测试代码,我已经对其进行了更新以检查动画,从而无需阅读它。 您可以使用唯一的ID来跟踪所有动画并将其存储在具有视图属性的字典中。 我没有实现这部分,但这就是我将要做的。 希望我足够了解您的问题。 另外,我使用Xcode 9,但不确定代码是否相同。 我更改了一些逻辑部分,所以让我知道是否可以解决问题。

UUID().uuidString //for unique string in implementation

//from your code just slightly altered.
//
//  ViewController.swift
//  CATest


//
//  CATestViewController.swift
//  SimpleCALayerTest


import UIKit

class CATestViewController: UIViewController, CAAnimationDelegate{


    var slider : UISlider!
    var animatableLayer : CALayer!

    var animationContainerView : UIView!

    var centerY : CGFloat!
    var startTranslationX : CGFloat!
    var endTranslationX : CGFloat!

    let duration = 10.0

    ///boring nibless view setup code
    override func loadView() {
        let marginX = CGFloat(10)
        let marginY = CGFloat(10)
        let view = UIView(frame: CGRect(x: 0, y: 0, width: 400, height: 400))

        view.backgroundColor = .lightGray

        slider = UISlider(frame: CGRect(x: marginX, y: 0, width: 200, height: 50))
        slider.maximumValue = Float(duration)
        slider.addTarget(self, action: #selector(sliderChanged(_:)), for: .valueChanged)

        slider.addTarget(self, action: #selector(sliderDragStart(_:)), for: .touchDown)
        slider.addTarget(self, action: #selector(sliderDragEnd(_:)), for: .touchUpInside)

        //A view to house an animated sublayer
        animationContainerView = UIView(frame: CGRect(x: marginX, y: 50, width: 200, height: 70))



        //add a play button that will allow the animation to be played without hindrance from the slider
        let playButton = UIButton(frame: CGRect(x: marginX, y: animationContainerView.frame.maxY + marginY, width: 200, height: 50))
        playButton.setTitle("Change Frame", for: .normal)
        playButton.addTarget(self, action: #selector(playAnimation), for: .touchUpInside)
        view.addSubview(playButton)

        //add a stopped ani button that will allow the animation to be played using slider
        let addStoppedAniButton = UIButton(frame: CGRect(x: playButton.frame.origin.x, y: playButton.frame.maxY + marginY, width: playButton.frame.width, height: playButton.frame.size.height))
        addStoppedAniButton.setTitle("Pause", for: .normal)
        addStoppedAniButton.addTarget(self, action: #selector(cmPauseTapped(_:)), for: .touchUpInside)
        view.addSubview(addStoppedAniButton)



        let animatableLayerWidth = animationContainerView.bounds.width / CGFloat(4)
        centerY = animationContainerView.bounds.midY
        startTranslationX = animatableLayerWidth / CGFloat(2)
        endTranslationX = animationContainerView.bounds.width - animatableLayerWidth / CGFloat(2)


        animationContainerView.backgroundColor = .white
        animationContainerView.layer.borderColor = UIColor.black.withAlphaComponent(0.5).cgColor
        animationContainerView.layer.borderWidth = 1

        view.addSubview(slider)
        view.addSubview(animationContainerView)

        //Now add a layer to animate to the container
        animatableLayer = CALayer()
        animatableLayer.backgroundColor = UIColor.yellow.cgColor
        animatableLayer.borderWidth = 1
        animatableLayer.borderColor = UIColor.black.withAlphaComponent(0.5).cgColor
        var r = animationContainerView.bounds.insetBy(dx: 0, dy: 4)
        r.size.width = animatableLayerWidth
        animatableLayer.frame = r
        animationContainerView.layer.addSublayer(animatableLayer)

        self.view = view
    }

    @objc func cmPauseTapped(_ sender : UIButton){
        if animatableLayer.speed == 0{
            resume()
        }else{
            pause()
        }
    }

    @objc func sliderChanged(_ sender: UISlider){
        if animatableLayer.speed == 0{
            let time = CFTimeInterval(sender.value)
            animatableLayer.speed = 0
            animatableLayer.timeOffset = time// offset
            print("Time changed \(time)")
        }
    }

    var animations = [CAAnimation]()

    func addAnimations(){
        let ani = CAAnimation()
        animations.append(ani)
    }

    @objc func sliderDragStart(_ sender: UISlider)
    {
        if animatableLayer.speed > 0{
            animatableLayer.speed = 0
        }
        addStoppedAnimation()
    }

    func pause(){
        //just updating slider
        if slider.value != Float(animatableLayer.timeOffset){
            UIView.animate(withDuration: 0.3, animations: {
                self.slider.setValue(Float(self.animatableLayer.timeOffset), animated: true)
            })
        }

        animatableLayer.timeOffset = animatableLayer.convertTime(CACurrentMediaTime(), from: nil)
        animatableLayer.speed = 0

    }

    func resume(){
        if let _ = animatableLayer.animationKeys()?.contains("an animation key"){
            animatableLayer.speed = 1.0;
            let pausedTime = animatableLayer.timeOffset
            animatableLayer.beginTime = 0.0;
            let timeSincePause = animatableLayer.convertTime(CACurrentMediaTime(), from: nil) - pausedTime
            animatableLayer.beginTime = timeSincePause;
            return
        }

        print("Drag End with need to readd animation")
        ani = createGroup()
        animatableLayer.speed = 1
        animatableLayer.add(ani, forKey: "an animation key")
        let time = CFTimeInterval(slider.value)
        animatableLayer.timeOffset = time
        animatableLayer.beginTime = CACurrentMediaTime()

    }
    @objc func sliderDragEnd(_ sender: UISlider){
        resume()
    }

    //Animations

    var ani : CAAnimationGroup!

    func createGroup() -> CAAnimationGroup
    {
        let ani = CAAnimationGroup()

        ani.isRemovedOnCompletion = false
        ani.duration = 10
        ani.delegate = self

        ani.animations = [createTranslationAnimation(),createColourAnimation()]
        return ani
    }
    func createTranslationAnimation() -> CAKeyframeAnimation
    {
        let ani = CAKeyframeAnimation(keyPath: "position")
        ani.delegate = self
        ani.isRemovedOnCompletion = false
        ani.duration = 10
        ani.values = [CGPoint(x:0,y:centerY),CGPoint(x:endTranslationX,y:centerY)]
        ani.keyTimes = [0,1]
        return ani
    }

    func createColourAnimation() -> CAKeyframeAnimation
    {
        let ani = CAKeyframeAnimation(keyPath: "backgroundColor")
        ani.delegate = self
        ani.isRemovedOnCompletion = false

        ani.duration = 10
        ani.values = [UIColor.red.cgColor,UIColor.blue.cgColor]
        ani.keyTimes = [0,1]

        return ani
    }

    func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
        print("Animation Stopped")
    }

    func animationDidStart(_ anim: CAAnimation) {
        print("Animation started")
    }

    func addStoppedAnimation()
    {
        if let _ = animatableLayer.animationKeys()?.contains("an animation key"){
            slider.value += 0.5
            sliderChanged(slider)
            return
            //we do not want to readd it
        }
        ani = createGroup()
        animatableLayer.speed = 0
        animatableLayer.add(ani, forKey: "an animation key")
        let time = CFTimeInterval(slider.value)
        animatableLayer.timeOffset = time
        animatableLayer.beginTime = CACurrentMediaTime()
    }

    @objc func playAnimation(){
      addStoppedAnimation()
    }

}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM