iosobjective-cswiftuitapgesturerecognizer

UITapGestureRecognizer - make it work on touch down, not touch up?


What I'm using the tap event for is very time-sensitive, so I'm curious if it's possible to make UITapGestureRecognizer activate when the user simply touches down, rather than requiring them to touch up as well?


Solution

  • 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 UIKit
    import UIKit.UIGestureRecognizerSubclass
    
    class 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.UIGestureRecognizerSubclass
    
    class 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.