Search code examples
iosswiftuiviewuinavigationcontroller

Animation completion block goes haywire after dismissing view


I have a detail viewcontroller that contains a repeating carousel with an animating image view like so:

func animateCarousel1(){
    UIView.animate(withDuration: 1, delay: 3, options: .curveEaseInOut, animations: 
        //image animation
    },completion: { (_) -> Void in
        print("animation 1 complete")
        self.animateCarousel2() 
                  })
    }
func animateCarousel2(){
    UIView.animate(withDuration: 1, delay: 3, options: .curveEaseInOut, animations: 
        //image animation
    },completion: { (_) -> Void in
        print("animation 2 complete")
        self.animateCarousel1() 
                  })
    }

Upon popping this view and returning to the parent view, I see that in the debug console, the functions are continuing to be called in the background, repeatedly and simultaneously, infinitely.

CPU usage also jumps to 90% in the simulator as well.

Is there some sort of deinit I need to do before popping the viewcontroller? I can't seem to wrap my head around this one.


Solution

  • The issue is that completion blocks are called even if the animation finishes. So there are a few possible solutions:

    1. Check the finished parameter of the completion handlers:

      func animateCarousel1() {
          UIView.animate(withDuration: 1, delay: 3, options: .curveEaseInOut, animations: {
              //image animation
          }, completion: { finished in
              print("animation 1 complete")
              if finished { self.animateCarousel2() }
          })
      }
      
      func animateCarousel2() {
          UIView.animate(withDuration: 1, delay: 3, options: .curveEaseInOut, animations: {
              //image animation
          }, completion: { finished in
              print("animation 2 complete")
              if finished { self.animateCarousel1() }
          })
      }
      
    2. Use a different animation technique that doesn't require the circular references between these animation routines, e.g.:

      func animateCarousel() {
          UIView.animateKeyframes(withDuration: 8, delay: 3, options: .repeat, animations: {
              UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.125) {
                  // animation 1
              }
              UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.125) {
                  // animation 2
              }
          }, completion: nil)
      }