UITapGestureRecognizer - make it work on touch down, not touch up?
Use a UILongPressGestureRecognizer and set its minimumPressDuration
to 0. It will act like a touch down during the UIGestureRecognizerStateBegan
state.
For Swift 4+
func setupTap() { let touchDown = UILongPressGestureRecognizer(target:self, action: #selector(didTouchDown)) touchDown.minimumPressDuration = 0 view.addGestureRecognizer(touchDown)}@objc func didTouchDown(gesture: UILongPressGestureRecognizer) { if gesture.state == .began { doSomething() }}
For Objective-C
-(void)setupLongPress{ self.longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(didLongPress:)]; self.longPress.minimumPressDuration = 0; [self.view addGestureRecognizer:self.longPress];}-(void)didLongPress:(UILongPressGestureRecognizer *)gesture{ if (gesture.state == UIGestureRecognizerStateBegan){ [self doSomething]; }}
Create your custom TouchDownGestureRecognizer subclass and implement gesture in touchesBegan:
TouchDownGestureRecognizer.h
#import <UIKit/UIKit.h>@interface TouchDownGestureRecognizer : UIGestureRecognizer@end
TouchDownGestureRecognizer.m
#import "TouchDownGestureRecognizer.h"#import <UIKit/UIGestureRecognizerSubclass.h>@implementation TouchDownGestureRecognizer-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ if (self.state == UIGestureRecognizerStatePossible) { self.state = UIGestureRecognizerStateRecognized; }}-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{ self.state = UIGestureRecognizerStateFailed;}-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{ self.state = UIGestureRecognizerStateFailed;}@end
implementation:
#import "TouchDownGestureRecognizer.h" TouchDownGestureRecognizer *touchDown = [[TouchDownGestureRecognizer alloc] initWithTarget:self action:@selector(handleTouchDown:)]; [yourView addGestureRecognizer:touchDown];-(void)handleTouchDown:(TouchDownGestureRecognizer *)touchDown{ NSLog(@"Down");}
Swift implementation:
import UIKitimport UIKit.UIGestureRecognizerSubclassclass TouchDownGestureRecognizer: UIGestureRecognizer{ override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent) { if self.state == .Possible { self.state = .Recognized } } override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent) { self.state = .Failed } override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent) { self.state = .Failed }}
Here is the Swift syntax for 2017 to paste:
import UIKit.UIGestureRecognizerSubclassclass SingleTouchDownGestureRecognizer: UIGestureRecognizer { override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) { if self.state == .possible { self.state = .recognized } } override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) { self.state = .failed } override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) { self.state = .failed }}
Note that this is a drop-in replacement for UITap
. So in code like...
func add(tap v:UIView, _ action:Selector) { let t = UITapGestureRecognizer(target: self, action: action) v.addGestureRecognizer(t)}
you can safely swap to....
func add(hairtriggerTap v:UIView, _ action:Selector) { let t = SingleTouchDownGestureRecognizer(target: self, action: action) v.addGestureRecognizer(t)}
Testing shows it will not be called more than once. It works as a drop-in replacement; you can just swap between the two calls.
Swift (without subclassing)
Here is a Swift version similar to Rob Caraway's Objective-C answer.
The idea is to use a long press gesture recognizer with the minimumPressDuration
set to zero rather than using a tap gesture recognizer. This is because the long press gesture recognizer reports touch began events while the tap gesture does not.
import UIKitclass ViewController: UIViewController { @IBOutlet weak var myView: UIView! override func viewDidLoad() { super.viewDidLoad() // Add "long" press gesture recognizer let tap = UILongPressGestureRecognizer(target: self, action: #selector(tapHandler)) tap.minimumPressDuration = 0 myView.addGestureRecognizer(tap) } // called by gesture recognizer @objc func tapHandler(gesture: UITapGestureRecognizer) { // handle touch down and touch up events separately if gesture.state == .began { // do something... print("tap down") } else if gesture.state == .ended { // optional for touch up event catching // do something else... print("tap up") } }}