简体   繁体   中英

iPhone iOS how to add a UILongPressGestureRecognizer and UITapGestureRecognizer to the same control and prevent conflict?

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!

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];

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;
}

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;

}

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM