Tap Gesture on animating UIView not working

Viewed 6504

I have a tap gesture on a UILabel who's translation is being animated. Whenever you tap on the label during the animation there's no response from the tap gesture.

Here's my code:

    label.addGestureRecognizer(tapGesture)

    label.userInteractionEnabled = true
    label.transform = CGAffineTransformMakeTranslation(0, 0)

    UIView.animateWithDuration(12, delay: 0, options: UIViewAnimationOptions.AllowUserInteraction, animations: { () -> Void in
        label.transform = CGAffineTransformMakeTranslation(0, 900)
        }, completion: nil)

Gesture code:

func setUpRecognizers() {
    tapGesture = UITapGestureRecognizer(target: self, action: "onTap:")
}
func onTap(sender : AnyObject) {
    print("Tapped")
}

Any ideas? Thanks :)


Note added for 2021:

These days this is dead easy, you just override hitTest.

6 Answers

How to detect touches in a view which is moving

override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {

    let pf = layer.presentation()!.frame
    // note, that is in the space of our superview

    let p = self.convert(point, to: superview!)

    if pf.contains(p) { return self }
    return nil
}

It's that easy

Related tip -

Don't forget that in most cases if an animation is running, you will, of course, almost certainly want to cancel it. So, say there's a "moving target" and you want to be able to grab it with your finger and slide it somewhere else, naturally in that use case your code in your view controller will look something like ..

   func sliderTouched() {
       if alreadyMoving {
          yourPropertyAnimator?.stopAnimation(true)
          yourPropertyAnimator = nil
       }
       etc ...
   }

I was stuck on this problem for hours, and could not understand why the tapping did not work on an animated label which slides out of screen after 3 seconds delay.

Well said agibson007, about the animation works like a fake movie's playback, the 3-second delay controls the payback of the movie, yet the label's frame is changed as soon as the animation begins without a delay. So the tapping (which depends on the label's frame at its original position) would not work.

My solution was changing the 3-second delay to a timeout function like -

DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
   [weak self] in
   self?.hideLabel()           
}

So that keeps the tapping works during the delay, and allow animation runs inside the hideLabel() call after the delay.

Related