Ivan Cantarino
Ivan Cantarino

Reputation: 3246

Best way to execute multiple animations synchronously

This will be a 'best way to do' kind of a question.

I know there are multiple ways to do sequence animations, but which one is the better way to do it?

let's say I have 3 animations that I'm trying to execute, one after the other, so the next one only starts when the previous has finished.

I can do it with the UIView.animate(withCompletionBlock:) so in the completion section I call the second one and on the completionBlock of the second one I call the third one.

This one in particular makes me think: Is that a 'beautiful' way to do it? Is there any known issues using completionBlocks within another?

Just a thought. Calling animations within the previous animation completion block, may bring the code somehow like the pyramid of doom, if there's something like 10 synchronous animations, don't you think?

Another way to do is with the dispatch_after, the I call the next animation based on the NSTimeInterval of the previous one. In this case it may cause a app crash suspending the Main thread for the next one to execute, then suspend it again to execute the third one.

The last known way that I know is that I can use a NSTimeInterval to call the next animation, but this one is somewhat like the dispatch_after option.

In your opinion which would be the best way to do it? Executing 3 or more visual animations, all synchronously (waiting for the previous one to finish to start the next one).

I might use this a lot in my app and I want to start it with the best way.

EXAMPLES

Animations within completionBlocks

DispatchQueue.main.async {
        UIView.animate(withDuration: 2.0, animations: { 
            view1.alpha = 1
        }, completion: { (_) in
            UIView.animate(withDuration: 5.0, animations: {
                view2.alpha = 1
            }, completion: { (_) in
                UIView.animate(withDuration: 2.0, animations: {
                    view3.alpha = 0
                }, completion: { (_) in
                    UIView.animate(withDuration: 2.0, animations: {
                        view4.alpha = 0
                    }, completion: { (_) in
                            print("all completed")
                    })
                })
            })
        })
    }

With dispatch_after:

    let anim1_duration = 2.0
    let anim2_duration = 3.0
    let anim3_duraiton = 5.0

    UIView.animate(withDuration: anim1_duration, animations: { 
        view1.alpha = 1
    }

    DispatchQueue.main.asyncAfter(deadline: DispatchTime.init(uptimeNanoseconds: UInt64(anim1_duration * Double(NSEC_PER_SEC)))) {
        UIView.animate(withDuration: anim2_duration, animations: { 
            view2.alpha = 1
        }
    }

    DispatchQueue.main.asyncAfter(deadline: DispatchTime.init(uptimeNanoseconds: UInt64((anim1_duration + anim2_duration) * Double(NSEC_PER_SEC)))) {
        UIView.animate(withDuration: anim3_duration, animations: { 
            view3.alpha = 1
        }
    }

Upvotes: 3

Views: 2432

Answers (2)

Felix Lapalme
Felix Lapalme

Reputation: 1068

Check out UIView's animate​Keyframes(with​Duration:​delay:​options:​animations:​completion:​):

class func animateKeyframes(withDuration duration: TimeInterval, 
                      delay: TimeInterval, 
                    options: UIViewKeyframeAnimationOptions = [], 
                 animations: @escaping () -> Void, 
                 completion: ((Bool) -> Void)? = nil)

For your specific case, it would look like this:

UIView.animateKeyframes(withDuration: 20,
                        delay: 0,
                        options: [],
                        animations: {

                            UIView.addKeyframe(withRelativeStartTime: 0,
                                               relativeDuration: 0.2,
                                               animations: {
                                view1.alpha = 1
                            })

                            UIView.addKeyframe(withRelativeStartTime: 0.2,
                                               relativeDuration: 0.3,
                                               animations: {
                                view2.alpha = 1
                            })

                            UIView.addKeyframe(withRelativeStartTime: 0.5,
                                               relativeDuration: 0.1,
                                               animations: {
                                view3.alpha = 0
                            })

                            UIView.addKeyframe(withRelativeStartTime: 0.6,
                                               relativeDuration: 0.4,
                                               animations: {
                                view4.alpha = 0
                            })
},
                        completion: nil)

Upvotes: 3

Tung Fam
Tung Fam

Reputation: 8147

In terms of 'better reading code' consider these 2 ways of performing animations:

  1. building stacks of animations: more

  2. create keyframe animations using CAKeyframeAnimation: more

These options are avoiding completion blocks, which I like.

Upvotes: 0

Related Questions