22

I'm building an iPhone app that would let the user rearrange some of the UI elements on the screen.

How can I add a tap gesture recognizer and a long press gesture recognizer to the same UIView? When I lift up the finger from the long press, the tap gesture recognizer fires. How can I temporarily disable the tap gesture recognizer or prevent it from firing when the user is performing a long press?

Thank you!

Alex Stone
  • 46,408
  • 55
  • 231
  • 407

5 Answers5

56

To allow both gestures to work together, implement the following delegate method:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
    return YES;
}

To make it so that the long press has first priority, do:

[tapGesture requireGestureRecognizerToFail:longPress];

Snowman
  • 31,411
  • 46
  • 180
  • 303
11

To combine successfully both you need:

1º Add to interface gesture delegate at header

@interface ViewController : ViewController <UIGestureRecognizerDelegate>

2º Create gesture events and add to a view into source file:

UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(touch:)];
    [tap setNumberOfTapsRequired:1]; // Set your own number here
    [tap setDelegate:self]; // Add the <UIGestureRecognizerDelegate> protocol

    UILongPressGestureRecognizer *longTap = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longTouch:)];
    [longTap setNumberOfTapsRequired:0]; // Set your own number here
    [longTap setMinimumPressDuration:1.0];
    [longTap setDelegate:self]; // Add the <UIGestureRecognizerDelegate> protocol
    [tap requireGestureRecognizerToFail:longTap];   // Priority long

    [self.view addGestureRecognizer:tap];
    [self.view addGestureRecognizer:longTap];

3º Add callbacks in source file:

- (void) touch: (UITapGestureRecognizer *)recognizer
{
    CGPoint location = [recognizer locationInView: self.HUDview];
    if (recognizer.state == UIGestureRecognizerStateBegan)
    {
        NSLog(@"touch UIGestureRecognizerStateBegan");
    }
    if (recognizer.state == UIGestureRecognizerStateEnded)
    {
        NSLog(@"touch UIGestureRecognizerStateEnded");
        //NSLog(@"Position of touch: %.3f, %.3f", location.x, location.y);    // Position landscape
    }
}

- (void) longTouch: (UILongPressGestureRecognizer *)recognizer
{
    if (recognizer.state == UIGestureRecognizerStateBegan)
    {
        NSLog(@"longTouch UIGestureRecognizerStateBegan");
    }
    if (recognizer.state == UIGestureRecognizerStateEnded)
    {
        NSLog(@"longTouch UIGestureRecognizerStateEnded");
    }
}

4º Set gesture recognizer available:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    return YES;
}
vgonisanz
  • 11,831
  • 13
  • 78
  • 130
8

As an alternative approach, don't have two separate recognisers - just use the LongPress recogniser for both events:

Configure as follows:

UILongPressGestureRecognizer* longPress = [ [ UILongPressGestureRecognizer alloc ] initWithTarget:self.nextResponder action:@selector(longPressEvent:)];
    categoryPanelDrag.minimumPressDuration = 0.0;

Then handle as follows:

- (BOOL)longPressEvent:(UILongPressGestureRecognizer *)gesture {

    // _dragStarted is a class-level BOOL

    if(UIGestureRecognizerStateBegan == gesture.state) {
        _dragStarted = NO;
    }

    if(UIGestureRecognizerStateChanged == gesture.state) {
        _dragStarted = YES;
        // Do dragging stuff here
    }

    if(UIGestureRecognizerStateEnded == gesture.state) {

        if (_dragStarted == NO)
        {
            // Do tap stuff here

        }
        else
        {
            // Do drag ended stuff here
        }
    }

    return YES;

}
Journeyman
  • 10,011
  • 16
  • 81
  • 129
  • 1
    @Softlion don't forget the UIGestureRecognizerDelegate interface on the .h file :-) – Avi Levin Jul 26 '15 at 20:48
  • Well it compiles and run, but does not prevent conflict. I resolved the problem using 2 recognizers and gestureRecognizer(_:shouldRecognizeSimultaneouslyWithGestureRecognizer:) – Softlion Jul 27 '15 at 05:51
2

I did try moby and journeyman's approach but somehow they didn't fit my project well, so I solved like below,

-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
    NSLog(@"%@ %ld",touch.description, touch.phase);
    [self performSelector:@selector(checkTouch:) withObject:touch afterDelay:0.5];
    return YES;
}

and

- (void)checkTouch:(UITouch *)touch{
    NSLog(@"touch phase = %ld",touch.phase);
    if (touch.phase == UITouchPhaseStationary) {
        //still holding my hand and this means I wanted longPressTouch
    }
    if (touch.phase == UITouchPhaseEnded){
        //I released my finger so it's obviously tap 
    }
}

It could be simpler solution but of course it depends to project.

mert
  • 1,090
  • 13
  • 26
0

You could take care of it in the code, that during the long press, set a flag, and if the tap gets called while the flag is true or whatever then don't execute the tap code and reset the flag. I don't know a better way

Benoir
  • 1,244
  • 10
  • 10