Home > Enterprise >  How do I delay running a line of code until animations are finished? (DispatchGroup & DispatchQueue)
How do I delay running a line of code until animations are finished? (DispatchGroup & DispatchQueue)

Time:09-04

I'm working in Swift 5 (Xcode 13.4.1) and I've got 3 animations that should run after a button is pressed. One of the animations, 'rotate', is defined by a function. The rotate animation doesn't stop after duration ends, it just restarts. I'm trying to stop the rotation animation using self.UI_Image_Name.layer.removeAllAnimations.

I can't get my code to wait until animations are finished to run the removeAllAnimations line. I've been trying to follow this tutorial: Waiting until the task finishes

Here's my current code:

@IBAction func flipButtonPress(_ sender: UIButton) {
        
//        flipResult.image = results[Int.random(in: 0...1)]
        
        
        self.animationGroup.enter()
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now(), execute: { () -> Void in
        //Slide coin image to middle of screen
        UIView.animate(withDuration: 3, delay: 0,
                       options: [.curveEaseIn, .curveEaseOut],
                       animations: {
                            self.coinImage.frame.origin.y = 220
                       })
        //Rotate Coin image
            UIView.animate(withDuration: 11, delay: 3,
                           options: [.curveEaseIn, .curveEaseOut],
                           animations: {
                                self.coinImage.rotate()
            })
          //Fade away coin image
            UIView.animate(withDuration: 5, delay: 6,
                           options: [],
                           animations: {
                self.coinImage.alpha = 0.5
                                       
        })
            self.animationGroup.leave()
        })
        
        
        self.animationGroup.enter()
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now(), execute: { () -> Void in
            self.coinImage.layer.removeAllAnimations()
        })
        self.animationGroup.leave()
        
    }
    
}

extension UIImageView{
    func rotate() {
        let rotation : CABasicAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
        rotation.toValue = NSNumber(value: (Double.pi * 2) * 22)
        rotation.duration = 11
        rotation.isCumulative = true
        rotation.repeatCount = Float.greatestFiniteMagnitude
        rotation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
        rotation.beginTime = CACurrentMediaTime()   3;
        self.layer.add(rotation, forKey: "rotationAnimation")
    }
}

CodePudding user response:

Simplist Option

Okay, so there's a lot of stuff here, for one, you can remove the animation group, as if its only purpose is to help run code when the animations are finished, then we won't need it.

Secondly, animations come with a completion callback. So change your Fade away coin image to this:

UIView.animate(withDuration: 5, delay: 6, options: []) {
    self.coinImage.alpha = 0.5
} completion: { _ in
    self.coinImage.layer.removeAllAnimations()
}

This means that after the 6 second delay and the 5 second animation, the coins layer animations will be removed

Another Option

If this doesn't create the desired effect, then instead you can try this (I personally try to avoid using this where I can as it can be prone to breaking if animation times are changed):

DispatchQueue.main.asyncAfter(deadline: .now()   <#Delay in seconds#>) {
     <#code#>
}

This will run the code inside the closure after the given number of seconds.

  • Related