14

For example I have a view where I want two different gestures:

tap to do action A. double tap to do action B.

The problem is with UITapGestureRecognizer I can only set minimum required tap count. The single tap gesture recognizer recognizes a tap before the double tap gesture recognizer recognizes the double tap.

Example:

_tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGestureRecognized:)];
_tapGestureRecognizer.numberOfTouchesRequired = 1;
_tapGestureRecognizer.numberOfTapsRequired = 1;
[self addGestureRecognizer:_tapGestureRecognizer];

_doubleTapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(doubleTapGestureRecognized:)];
_doubleTapGestureRecognizer.numberOfTouchesRequired = 1;
_doubleTapGestureRecognizer.numberOfTapsRequired = 2;
[self addGestureRecognizer:_doubleTapGestureRecognizer];

It always recognizes the single tap even if I do double tap very fast. How can I set it up so the tap gesture recognizer waits and sees if the double tap gesture recognizer recognizes?

openfrog
  • 40,201
  • 65
  • 225
  • 373
  • Possible solution and duplicated: http://stackoverflow.com/questions/7175086/iphone-single-tap-gesture-conflicts-with-double-one – xarly Oct 01 '13 at 23:15
  • Try this to remove the delay or adjust it to which you want. http://stackoverflow.com/a/25368653/3060790 – Pagly Aug 18 '14 at 17:30

5 Answers5

49

Here is what I have used in one of my old projects, I hope it helps you out man.

UITapGestureRecognizer *singleTap = [[[UITapGestureRecognizer alloc] initWithTarget:    self action:@selector(doSingleTap)] autorelease];
singleTap.numberOfTapsRequired = 1; 
[self.view addGestureRecognizer:singleTap];

 UITapGestureRecognizer *doubleTap = [[[UITapGestureRecognizer alloc] initWithTarget:   self action:@selector(doDoubleTap)] autorelease];
 doubleTap.numberOfTapsRequired = 2; 
 [self.view addGestureRecognizer:doubleTap];

  [singleTap requireGestureRecognizerToFail:doubleTap];
Adrian P
  • 6,479
  • 4
  • 38
  • 55
  • 10
    this Works but, for Single Tap it Makes Delay on Call of Single tap – Mrug Mar 07 '14 at 08:01
  • 1
    ^^ Has anyone figured out a solution to this delay? – barfoon Aug 11 '14 at 18:04
  • For those like @barfoon who still have the delay to cancel the double tap, please refer to this solution by eladleb : http://stackoverflow.com/a/23415324/1158074 and use the subclass for the double tap gesture so it sends "state failed" faster. – Michael Pirotte Dec 04 '15 at 13:47
7

On swift 3 and swift 4:

let singleTap = UITapGestureRecognizer(target: self, action: #selector(doSingleTap))
singleTap.numberOfTapsRequired = 1

self.view.addGestureRecognizer(singleTap)

let doubleTap = UITapGestureRecognizer(target: self, action: #selector(doDoubleTap))
doubleTap.numberOfTapsRequired = 2

self.view.addGestureRecognizer(doubleTap)

singleTap.require(toFail: doubleTap)
pableiros
  • 14,932
  • 12
  • 99
  • 105
0
   {  UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(handleDoubleTapGesture:)];
        [doubleTap setDelegate:self];
        doubleTap.numberOfTapsRequired = 2;
        [self.headerView addGestureRecognizer:doubleTap];

        UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(handleSingleTapGesture:)];
        singleTap.numberOfTapsRequired = 1;
        [singleTap setDelegate:self];
        [doubleTap setDelaysTouchesBegan:YES];
        [singleTap setDelaysTouchesBegan:YES];
        [self.headerView addGestureRecognizer:singleTap];
  }  
        - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
            return  YES;
        }

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

Create the gesture recognizers inside any method you want. For example if you want tap gestures for your headerView in tableView create those tap gestures inside

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section

And your class should implement UIGestureRecognizerDelegate. Above mentioned are the 2 methods of that delegate class.

iPhoneDeveloper
  • 958
  • 1
  • 14
  • 23
0

Another option is to use UIGestureRecognizerDelegate, especially if you do not have easy access to the other recognizer.

let singleTap = UITapGestureRecognizer(target: self, action: #selector(doSingleTap))
singleTap.numberOfTapsRequired = 1
singleTap.delegate = self
self.view.addGestureRecognizer(singleTap)

Implement the delegate:

extension ViewController: UIGestureRecognizerDelegate {
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        if let tapGesture = gestureRecognizer as? UITapGestureRecognizer, let otherTapGesture = otherGestureRecognizer as? UITapGestureRecognizer {
            return tapGesture.numberOfTapsRequired == 1 && otherTapGesture.numberOfTapsRequired == 2
        }
        return false
    }
}
Marián Černý
  • 15,096
  • 4
  • 70
  • 83
-1

I found a solution to the delay of the single tap using XCode Monkey's answer. Using this approach, you can configure in mili-seconds the threshold between a single and a double tap. This technique uses a simple UIButton and a NSTimer to differentiate between single and double tap.

@property NSTimer       *tapTimer;
@property BOOL           isDoubleTap;

  UIButton *yourButton = [UIButton new];
  yourButton.frame = // set your frame
  [yourButton addTarget:self action:@selector(handleTap:) forControlEvents:UIControlEventTouchUpInside];
  [self.view yourButton];


-(void)handleTap:(UIButton*)button
{
  [self.tapTimer invalidate];

  if (self.isDoubleTap)
  {
    [self doDoubleTap];
    return;
  }
  self.isDoubleTap = YES;
  // CHANGE THE 0.3 SECOND THRESHOLD BETWEEN SINGLE/DOUBLE TAP TO YOUR HEART'S CONTENT
  self.tapTimer = [NSTimer scheduledTimerWithTimeInterval:0.3 target:self selector:@selector(doSingleTap) userInfo:nil repeats:NO];
}

-(void)doSingleTap
{
  self.isDoubleTap = NO;
}

-(void)doDoubleTap
{
  self.isDoubleTap = NO;
}
etayluz
  • 15,920
  • 23
  • 106
  • 151
  • 1
    No improvement in delay AND creates potential problems with users clicking multiple cells in less than .3 seconds – ChuckKelly Oct 08 '15 at 06:21
  • You either have to keep a significant (0.2) delay or else double tap detection fails to register before single tap timer expires. – Jeet May 19 '16 at 07:22