UITapGestureRecognizer - make it work on touch down, not touch up?
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.
iOS Detect tap down and touch up of a UIView
A Gesture Recognizer is probably overkill for what you want. You probably just want to use a combination of -touchesBegan:withEvent:
and -touchesEnded:withEvent:
.
This is flawed, but it should give you and idea of what you want to do.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
self.touchDown = YES;
self.backgroundColor = [UIColor redColor];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
// Triggered when touch is released
if (self.isTouchDown) {
self.backgroundColor = [UIColor whiteColor];
self.touchDown = NO;
}
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
// Triggered if touch leaves view
if (self.isTouchDown) {
self.backgroundColor = [UIColor whiteColor];
self.touchDown = NO;
}
}
This code should go in a custom subclass of UIView
that you create. Then use this custom view type instead of UIView
and you'll get touch handling.
I add a UIGestureReconizer to a view to detect Touch Down action, but then the buttons above the view won't work anymore
You could compare that event in touchesBegan has touches for the view that is the same as the view to witch your gesture recognizer was assigned
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
guard let view = self.view else { return }
guard let touches = event.touches(for: view) else { return }
super.touchesBegan(touches, with: event)
if self.state == .possible {
self.state = .recognized
}
}
Touch up and Touch down action for UIImageView
Here is the solution:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
if touch.view == self {
//began
}
}
super.touchesBegan(touches, with: event)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
if touch.view == self {
//end
}
}
super.touchesEnded(touches, with: event)
}
Note: Put this inside a UIView SubClass and add: userInteractionEnabled = true
inside the init block
How to Implement Touch Up Inside in touchesBegan, touchesEnded
Using the UILongPressGesturizer
is actually a much better solution to mimic all of the functionality of a UIButton
(touchUpInside
, touchUpOutside
, touchDown
, etc.):
- (void) longPress:(UILongPressGestureRecognizer *)longPressGestureRecognizer
{
if (longPressGestureRecognizer.state == UIGestureRecognizerStateBegan || longPressGestureRecognizer.state == UIGestureRecognizerStateChanged)
{
CGPoint touchedPoint = [longPressGestureRecognizer locationInView: self];
if (CGRectContainsPoint(self.bounds, touchedPoint))
{
[self addHighlights];
}
else
{
[self removeHighlights];
}
}
else if (longPressGestureRecognizer.state == UIGestureRecognizerStateEnded)
{
if (self.highlightView.superview)
{
[self removeHighlights];
}
CGPoint touchedPoint = [longPressGestureRecognizer locationInView: self];
if (CGRectContainsPoint(self.bounds, touchedPoint))
{
if ([self.delegate respondsToSelector:@selector(buttonViewDidTouchUpInside:)])
{
[self.delegate buttonViewDidTouchUpInside:self];
}
}
}
}
Multiple UITapGestureRecognizer not working on UIScrollView
A tap gesture only has one state - "ended". You can't detect when a tap starts using a tap gesture. As you've seen, attempting to use two tap gestures doesn't accomplish what you want.
You need to implement the UIResponder
methods touchesBegan
and touchesEnded
.
You may also want to see UITapGestureRecognizer - make it work on touch down, not touch up?
.
How do you detect a SwiftUI touchDown event with no movement or duration?
If you combine the code from these two questions:
How to detect a tap gesture location in SwiftUI?
UITapGestureRecognizer - make it work on touch down, not touch up?
You can make something like this:
ZStack {
Text("Test")
TapView {
print("Tapped")
}
}
struct TapView: UIViewRepresentable {
var tappedCallback: (() -> Void)
func makeUIView(context: UIViewRepresentableContext<TapView>) -> TapView.UIViewType {
let v = UIView(frame: .zero)
let gesture = SingleTouchDownGestureRecognizer(target: context.coordinator,
action: #selector(Coordinator.tapped))
v.addGestureRecognizer(gesture)
return v
}
class Coordinator: NSObject {
var tappedCallback: (() -> Void)
init(tappedCallback: @escaping (() -> Void)) {
self.tappedCallback = tappedCallback
}
@objc func tapped(gesture:UITapGestureRecognizer) {
self.tappedCallback()
}
}
func makeCoordinator() -> TapView.Coordinator {
return Coordinator(tappedCallback:self.tappedCallback)
}
func updateUIView(_ uiView: UIView,
context: UIViewRepresentableContext<TapView>) {
}
}
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
}
}
There's definitely some abstractions we can make so that the usage is more like the other SwiftUI Gestures, but this is a start. Hopefully Apple builds in support for this at some point.
UITapGestureRecognizer Activate on Immediate Touch?
You need to implement the methods (touchesBegan: withEvent: etc) to detect the received touch in your UIViewController and check if the touch corresponds to the UIView of your interest. And from there you can trigger an action.
Check this thread how to implement the methods
Or this one
Touch down on UITapGestureRecogniser
No, that's not possible, but it shouldn't be hard to subclass UIGestureRecognizer
to create your own recognizer that does that.
You could also use a UILongPressGestureRecognizer
with the minimumPressDuration
property set to 0.0
. Note however that your action will then be called continuously when the touch moves, so make sure to check that the state
of the recognizer is UIGestureRecognizerStateBegan
in the action (this will only be once).
Resolve UITapGestureRecognizer and UILongPressGestureRecognizer simultaneously and fire UIGestureRecognizer.State.began on finger touch down
I wonder if your implementation of shouldRequireFailureOf
is causing an issue?
This works just fine for me (note: I used .minimumPressDuration = 0.25
because it's a little difficult to tap in under 0.1 seconds):
class GestureTestViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
setupTapGestures()
}
private func setupTapGestures() -> Void {
let singleTapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTapGesture(_:)))
view.addGestureRecognizer(singleTapGesture)
let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPressGesture(_:)))
longPressGesture.minimumPressDuration = 0.25
view.addGestureRecognizer(longPressGesture)
}
@objc func handleLongPressGesture(_ gesture: UILongPressGestureRecognizer) -> Void {
if gesture.state == .began {
print("Did Long Press (began)")
}
if gesture.state == .ended {
print("Did Long Press (ended)")
}
}
@objc func handleTapGesture(_ gesture: UITapGestureRecognizer) -> Void {
print("Did Single Tap")
}
}
When I tap, I get "Did Single Tap" in the debug console.
When I tap and hold, I quickly get "Did Long Press (began)", and on finger-lift I get "Did Long Press (ended)"
Related Topics
Swift: Extra Argument 'Error' in Call
Create Singleton Using Gcd'S Dispatch_Once in Objective-C
Can't Use Swift Classes Inside Objective-C
Ios 9 Xcode 7 - Application Appears With Black Bars on Top and Bottom
Ios Upload Image and Text Using Http Post
Figure Out Size of Uilabel Based on String in Swift
Nsurlsession/Nsurlconnection Http Load Failed on iOS 9
Evenly Space Multiple Views Within a Container View
How to Force a Uiviewcontroller to Portrait Orientation in iOS 6
Ios 9 Not Opening Instagram App With Url Scheme
How to Present Uialertcontroller When Not in a View Controller
How to Find Topmost View Controller on Ios
Presenting Modal in iOS 13 Fullscreen
Programmatically Get Own Phone Number in Ios
How to Load Image from Local Path iOS Swift (By Path)