73

I have a UIPanGestureRecognizer I am using to track an object (UIImageView) below a user's finger. I only care about motion on the X axis, and if the touch strays above or below the object's frame on the Y axis I want to end the touch.

I've got everything I need for determining if a touch is within the object's Y bounds, but I don't know how to cancel the touch event. Flipping the recognizer's cancelsTouchesInView property doesn't seem to do what I want.

Thanks!

giampaolo
  • 6,906
  • 5
  • 45
  • 73
Josh French
  • 973
  • 1
  • 6
  • 12

7 Answers7

163

This little trick works for me.

@implementation UIGestureRecognizer (Cancel)

- (void)cancel {
    self.enabled = NO;
    self.enabled = YES;
}

@end

From the UIGestureRecognizer @enabled documentation:

Disables a gesture recognizers so it does not receive touches. The default value is YES. If you change this property to NO while a gesture recognizer is currently recognizing a gesture, the gesture recognizer transitions to a cancelled state.

Matej Bukovinski
  • 6,152
  • 1
  • 36
  • 36
15

@matej's answer in Swift.

extension UIGestureRecognizer {
  func cancel() {
    isEnabled = false
    isEnabled = true
  }
}
Pavel Alexeev
  • 6,026
  • 4
  • 43
  • 51
Hlung
  • 13,850
  • 6
  • 71
  • 90
11

Obj-C:

recognizer.enabled = NO;
recognizer.enabled = YES;

Swift 3:

recognizer.isEnabled = false
recognizer.isEnabled = true
Michael
  • 9,639
  • 3
  • 64
  • 69
budiDino
  • 13,044
  • 8
  • 95
  • 91
4

How about this from the apple docs:

@property(nonatomic, getter=isEnabled) BOOL enabled

Disables a gesture recognizers so it does not receive touches. The default value is YES. If you change this property to NO while a gesture recognizer is currently recognizing a gesture, the gesture recognizer transitions to a cancelled state.

Ying
  • 1,944
  • 5
  • 24
  • 38
3

According to the documentation you can subclass you gesture recogniser:

In YourPanGestureRecognizer.m:

#import "YourPanGestureRecognizer.h"

@implementation YourPanGestureRecognizer

- (void) cancelGesture {
    self.state=UIGestureRecognizerStateCancelled;
}

@end

In YourPanGestureRecognizer.h:

#import <UIKit/UIKit.h>
#import <UIKit/UIGestureRecognizerSubclass.h>

@interface NPPanGestureRecognizer: UIPanGestureRecognizer

- (void) cancelGesture;

@end

Now you can call if from anywhere

YourPanGestureRecognizer *panRecognizer = [[YourPanGestureRecognizer alloc] initWithTarget:self action:@selector(panMoved:)];
[self.view addGestureRecognizer:panRecognizer];
[...]
-(void) panMoved:(YourPanGestureRecognizer*)sender {
    [sender cancelGesture]; // This will be called twice
}

Ref: https://developer.apple.com/documentation/uikit/uigesturerecognizer?language=objc

Rémy Blanc
  • 313
  • 2
  • 9
3

Just set recognizer.state in your handlePan(_ recognizer: UIPanGestureRecognizer) method to .ended or .cancelled

Anatoly
  • 111
  • 1
  • 3
0

You have a couple ways of handling this:

  1. If you were writing a custom pan gesture recognizer subclass, you could easily do this by calling -ignoreTouch:withEvent: from inside the recognizer when you notice it straying from the area you care about.

  2. Since you're using the standard Pan recognizer, and the touch starts off OK (so you don't want to prevent it with the delegate functions), you really can only make your distinction when you receive the recognizer's target actions. Check the Y value of the translationInView: or locationInView: return values, and clamp it appropriately.

Ben Zotto
  • 70,108
  • 23
  • 141
  • 204
  • 3
    the question is that how do you ignore the recognizer and pass touch events to others in its action? – Jack Sep 11 '11 at 01:54