Search code examples
iosswiftcore-animation

Swift - CoreAnimation with duration


Trying to understand CoreAnimation and completion handlers.
Why does this code happen all at once, not over a 5.0 second period?
I am trying to get it to run 6 times with the 5 second duration each time for a total of 30 seconds and get "complete" to print every 5 seconds.

    func animateBox() {

        UIView.animate(withDuration: 5.0, delay: 0.0, options: [], animations: {
            self.myBox.layer.borderColor = self.getRandom()
        }, completion: {finished in
            print("complete")
        })
    
    }

    @objc func buttonTapped() {
        
        for _ in 1...6 {
            animateBox()
            print("animate")
        }
    }

Solution

  • for _ in 1...6 { is executed instantly, so animateBox is called 6 times, right after another, in a split second.

    What you want to do is call each animation block inside the completion handler (which is called when the animation completes) of the previous one. Something like this:

    UIView.animate(withDuration: 5.0, delay: 0.0, options: [], animations: {
        self.myBox.layer.borderColor = self.getRandom()
    }, completion: { finished in
        print("complete")
    
        UIView.animate(withDuration: 5.0, delay: 0.0, options: [], animations: {
            self.myBox.layer.borderColor = self.getRandom()
        }, completion: { finished in
            print("complete")
    
            UIView.animate(withDuration: 5.0, delay: 0.0, options: [], animations: {
                self.myBox.layer.borderColor = self.getRandom()
            }, completion: { finished in
                print("complete")
    
                ...
            })
        })
    })
    

    But this will result in a huge completion pyramid... instead, try using animateKeyframes:

    let totalDuration = CGFloat(30)
    let relativeIndividualDuration = CGFloat(1) / CGFloat(6)
    
    UIView.animateKeyframes(withDuration: totalDuration, delay: 0, options: .calculationModeCubic, animations: {
        UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: relativeIndividualDuration) {
            self.myBox.layer.borderColor = self.getRandom()
        }
    
        UIView.addKeyframe(withRelativeStartTime: relativeIndividualDuration, relativeDuration: relativeIndividualDuration) {
            self.myBox.layer.borderColor = self.getRandom()
        }
    
        UIView.addKeyframe(withRelativeStartTime: relativeIndividualDuration * 2, relativeDuration: relativeIndividualDuration) {
            self.myBox.layer.borderColor = self.getRandom()
        }
    
        ...
    })