iOS animation doesn't reset when replayed repeatedly

Viewed 108

I have a very simple animation block which performs the following animation:

enter image description here
(tap the image if the animation doesn't play)

func didTapButton() {
  // reset the animation to 0
  centerYConstraint.constant = 0
  superview!.layoutIfNeeded()

  UIView.animate(
    withDuration: 1,
    animations: {
      // animate the view downwards 30 points
      self.centerYConstraint.constant = 30
      self.superview!.layoutIfNeeded()
  })
}

Everything is great when I play the animation by itself. It resets to position 0, then animates 30 points.

The problem is when the user taps the button multiple times quickly (i.e. during the middle of an ongoing animation). Each time the user taps the button, I would expect it to reset to position 0, then animate downwards 30 points. Instead, I get this behavior:

enter image description here
(tap the image if the animation doesn't play)

It's clearly traveling well over 30 points. (Closer to 120 points.)

Why is this happening, and how can I "reset" the animation properly so that it only at most travels 30 points?

Things that I have tried that didn't work:

  • Using options: UIViewAnimationOptions.beginFromCurrentState. It does the same exact behavior.
  • Instead of executing the animation directly, do it after a few milliseconds using dispatch_after. Same behavior.
  • "Canceling" the previous animation by using a 0 second animation that changes the constant to 0, and calls superview!.layoutIfNeeded.

Other notes:

  • If instead of changing the position, I change the height via a constraint, I get similar odd behavior. E.g. if I set it to go from 30 points to 15 points, when repeatedly pressing the button, the view will clearly grow up to around 120 points.
  • Even if I don't use constraints, and instead I animate the transform from CGAffineTransform.identity to CGAffineTransform(scaleX: 0.5, y: 0.5), I get the same behavior where it'll grow to 120 points.
  • If I try a completely different animatable property say backgroundColor = UIColor(white: 0, alpha: 0.5) to backgroundColor = UIColor(white: 0, alpha: 0), then I get correct behavior (i.e. each time I tap the button, the color resets to 0.5 gray at most. It never gets to black.
2 Answers
Related