Staggered animations with CAKeyframeAnimation?

前端 未结 2 1502
轮回少年
轮回少年 2021-01-16 03:55

I want to animate 3 different images at specific point in time such that it behaves this way.

1) 1st image moves from (Xx, Yx) to (Xz,Yz) 
2) Wait 10 seconds         


        
相关标签:
2条回答
  • 2021-01-16 04:16

    Edited

    When I wrote this, I thought you could not use a CAAnimationGroup to animate multiple layers. Matt just posted an answer demonstrating that you can do that. I hereby eat my words.

    I've taking the code in Matt's answer and adapted it to a project which I've uploaded to Github (link.)

    The effect Matt's animation creates is of a pair of feet walking up the screen. I found some open source feet and installed them in the project, and made some changes, but the basic approach is Matt's. Props to him.

    Here is what the effect looks like:

    (The statement below is incorrect) No, you can't use a keyframe animation to animate multiple layers. A given CAAnimation can only act on a single layer. This includes group layers, by the way.

    If all you're doing is things like moving images on a straight line, fading out, and fading in, why don't you use UIView animation? Take a look at the methods who's names start with animateWithDuration:animations: Those will let you create multiple animations at the same time, and the completion block can then trigger additional animations.

    If you need to use layer animation for some reason, you can use the beginTime property (which CAAnimation objects have because they conform to the CAMediaTiming protocol.) For CAAnimations that are not part of an animation group, you use

    animation.beginTime = CACurrentMediaTime() + delay;
    

    Where delay is a double which expresses the delay in seconds.

    If the delay is 0, the animation would begin.

    A third option would be to set your view controller up as the delegate of the animation and use the animationDidStop:finished: method to chain your animations. This ends up being the messiest approach to implement, in my opinion.

    0 讨论(0)
  • 2021-01-16 04:31

    The claim that a single animation group cannot animate properties of different layers is not true. It can. The technique is to attach the animation group to the superlayer and refer to the properties of the sublayers in the individual animations' key paths.

    Here is a complete example just for demonstration purposes. When launched, this project displays two "footprints" that proceed to step in alternation, walking off the top of the screen.

    class ViewController: UIViewController, CAAnimationDelegate {
    
        let leftfoot = CALayer()
        let rightfoot = CALayer()
    
        override func viewDidLoad() {
            super.viewDidLoad()
            
            self.leftfoot.name = "left"
            self.leftfoot.contents = UIImage(named:"leftfoot")!.cgImage
            self.leftfoot.frame = CGRect(x: 100, y: 300, width: 50, height: 80)
            self.view.layer.addSublayer(self.leftfoot)
    
            self.rightfoot.name = "right"
            self.rightfoot.contents = UIImage(named:"rightfoot")!.cgImage
            self.rightfoot.frame = CGRect(x: 170, y: 300, width: 50, height: 80)
            self.view.layer.addSublayer(self.rightfoot)
    
            DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
                self.start()
            }
        }
    
        func start() {
            let firstLeftStep = CABasicAnimation(keyPath: "sublayers.left.position.y")
            firstLeftStep.byValue = -80
            firstLeftStep.duration = 1
            firstLeftStep.fillMode = .forwards
    
            func rightStepAfter(_ t: Double) -> CABasicAnimation {
                let rightStep = CABasicAnimation(keyPath: "sublayers.right.position.y")
                rightStep.byValue = -160
                rightStep.beginTime = t
                rightStep.duration = 2
                rightStep.fillMode = .forwards
                return rightStep
            }
            func leftStepAfter(_ t: Double) -> CABasicAnimation {
                let leftStep = CABasicAnimation(keyPath: "sublayers.left.position.y")
                leftStep.byValue = -160
                leftStep.beginTime = t
                leftStep.duration = 2
                leftStep.fillMode = .forwards
                return leftStep
            }
    
            let group = CAAnimationGroup()
            group.duration = 11
            group.animations = [firstLeftStep]
            for i in stride(from: 1, through: 9, by: 4) {
                group.animations?.append(rightStepAfter(Double(i)))
                group.animations?.append(leftStepAfter(Double(i+2)))
            }
            group.delegate = self
            self.view.layer.add(group, forKey: nil)
        }
        
        func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
            print("done")
            self.rightfoot.removeFromSuperlayer()
            self.leftfoot.removeFromSuperlayer()
        }
    
    }
    

    Having said all that, I should add that if you are animating a core property like the position of something, it might be simpler to make it a view and use a UIView keyframe animation to coordinate animations on different views. Still, the point is that to say that this cannot be done with CAAnimationGroup is just wrong.

    0 讨论(0)
提交回复
热议问题