This question already has answers here:
UIButton not calling action in iOS 5 but works in iOS 6
(2 answers)
Closed 9 years ago.
For some reason, my UITapGestureRecognizer is not calling it's method when I tap a UIButton. What's really weird is that I used breakpoints in Xcode to make sure that the gestureRecognizer:shouldReceiveTouch: method is returning YES. The gesture should be calling it's method, but it isn't. I have cancelsTouchesInView set to YES, but it doesn't seem to do anything.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if ([touch.view isKindOfClass: [UIButton class]] && !editingTaskName)
return NO;
else if ([touch.view isKindOfClass: [UITextField class]])
return NO;
return YES; // handle the touch
}
Here's the code where I set up all my gesture recognizers. Maybe the others are interfering with my tap gesture.
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget: self action: #selector(longPress:)];
[longPress setMinimumPressDuration: 0.3];
[longPress setDelaysTouchesBegan: YES];
[self setLongPressGesture: longPress];
[[self tableView] addGestureRecognizer: longPress];
UITapGestureRecognizer *backToTableView = [[UITapGestureRecognizer alloc] initWithTarget: self action: #selector(backTapRecognized:)];
[backToTableView setCancelsTouchesInView: YES];
[backToTableView setDelegate: self];
[backToTableView setEnabled: NO];
[self setBackTapGesture: backToTableView];
[[self tableView] addGestureRecognizer: backToTableView];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget: self action: #selector(hideShowEditing:)];
[tap setCancelsTouchesInView: YES];
[tap setDelegate: self];
[self setEditTap: tap];
[[self tableView] addGestureRecognizer: tap];
A button has its own tappability, so there's a conflict between the button and the gesture recognizer. In iOS 6, button wins; the gesture recognizer is thus prevented from recognizing.
In iOS 6, there is a UIView gestureRecognizerShouldBegin: method. The UIButton returns NO (that's built-in), so that's the outcome of the conflict.
This takes even higher priority than your gestureRecognizer:shouldReceiveTouch:.
Try setting the numberOfTapsRequired property of your UITapGestureRecognizer. Something like this should suffice:
tap.numberOfTapsRequired = 1;
Max
Related
I have added tap gesture on UIImageView. I want to get the tag from it when I tap on the image. Please tell me how can I do it?
UITapGestureRecognizer *tapGesture1 = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tap_post_image:)];
tapGesture1.numberOfTapsRequired = 1
[tapGesture1 setDelegate:self];
[cell.beizer_image setUserInteractionEnabled:true];
[cell.beizer_image addGestureRecognizer:tapGesture1];
[cell.beizer_image setTag:indexPath.row];
- (void) tap_post_image: (id)sender
{
NSInteger the_tag = ((UIView*)sender).tag;
NSLog(#"tap post image is called");
NSLog(#"TAG is %ld",(long)the_tag);
}
Above code crashes the app.
While you really need to provide details of the crash on your question, one obvious issue is that you assume the sender parameter of your top_post_image: method is a UIView.
This is incorrect. The parameter will be the gesture recognizer, not a view. But you can get the view from the gesture.
The code should be:
- (void)tap_post_image:(UITapGestureRecognizer *)gesture {
NSInteger the_tag = gesture.view.tag;
}
On an unrelated note you need to work on your naming conventions. In Objective-C (as well as many other languages), it is common practice to use what is called "camel case'. Your method should be named tapPostImage:, not tap_post_image.
You will crash because sender in your code is not uiview, uiimageview.
sender is UITapGestureRecognizer class.
You should do
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapImageView:)];
[tapGesture setNumberOfTapsRequired:1];
[self.sampleImage setUserInteractionEnabled:YES];
[self.sampleImage addGestureRecognizer:tapGesture];
[self.sampleImage setTag:99];
- (void)tapImageView:(id)sender {
NSLog(#"Sender is a %# class",NSStringFromClass([sender class]));
UITapGestureRecognizer *tapGesture = ((UITapGestureRecognizer *)sender);
UIImageView *imageView = (UIImageView *)tapGesture.view;
NSLog(#"imageView.tag = %d",imageView.tag);
}
I hope it helps!
Today I tried to add an additional UILongPressGestureRecognizer to an UISwitch, which did not really work. My code is the following:
UILongPressGestureRecognizer *lpgr = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(doSomething:)];
[lpgr setDelaysTouchesBegan:YES];
[lpgr setDelaysTouchesEnded:YES];
[lpgr setCancelsTouchesInView:YES];
[self.switcher addGestureRecognizer:lpgr];
in the viewDidLoad method of the viewController that is the parent viewController of that switch. (the switcher is an instance variable, set through a storyboard. The IBOutlet is set properly from the UISwitch to the switcherProperty of its viewController).
On a couple of other controls (like a UIButton, UISlider, UIStepper and so on) this works, even without the touches cancelled or delayed, and perfectly triggers the target method. However, with my switch, it refuses that behavior.
I have tried removing any other gestureRecognizer on the UISwitch by iterating through all gestureRecognizers on that switch and calling [switcher removeGestureRecognizer: ... ], I´ve also set all of them to require my longPressGestureRecognizer to fail. Other types of gestureRecognizers don´t fire as well.
As far as I understand, GestureRecognizers will receive touches, before the view or control, which they belong to, receives them. Thus, setCancelsTouchesInView:YES and setDelaysTouchesInView:YES should enable the gestureRecognizer to handle every single gesture until it fails or succeeds, without the view knowing, right?
--------------EDIT------------------
The whole content of my method:
- (void)viewDidLoad
{
[super viewDidLoad];
UIGestureRecognizer *lpgr = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(edit:)];
[self.view addGestureRecognizer:lpgr];
lpgr = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(edit:)];
[self.button addGestureRecognizer:lpgr];
lpgr = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(edit:)];
[self.slider addGestureRecognizer:lpgr];
lpgr = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(edit:)];
[self.segmentedControl addGestureRecognizer:lpgr];
lpgr = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(edit:)];
[self.switcher addGestureRecognizer:lpgr];
lpgr = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(edit:)];
[self.stepper addGestureRecognizer:lpgr];
}
As I said, for the other controls this is working, only the switch does not want to work
try with it
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
// Disallow recognition of tap gestures in the segmented control.
if ((touch.view == yourSwitch)) {//change it to your condition
return NO;
}
return YES;
}
make sure you confirm UIGestureRecognizerDelegate for it to work.
In my app, I have an image and a UITextView.
I have created a UITapGestureRecognizer for both the views but the issue is that wherever I click on the screen, only the method associated with the UITextView gets executed.
Even if I click on the image, only the UITapGestureRecognizer method associated with the UITextView gets executed.
Following is the code I've implemented:
UITapGestureRecognizer *tapGestureRecognizerImage = [[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(handleTapFromImage:)];
[infobutton addGestureRecognizer:tapGestureRecognizerImage];
[[self view] addGestureRecognizer:tapGestureRecognizerImage];
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(handleTapFrom:)];
[messageOne addGestureRecognizer:tapGestureRecognizer];
[[self view] addGestureRecognizer:tapGestureRecognizer];
//The following are the methods associated
- (void) handleTapFrom: (UITapGestureRecognizer *)recognizer {
//Code to handle the gesture
NSLog(#"I am in handleTapFrom method");
}
- (void) handleTapFromImage: (UITapGestureRecognizer *)recognizer {
//Code to handle the gesture
NSLog(#"I am in handleTapFrom Image method");
[self.view makeToast:#"Your verification code does not match. Re-enter your verification code"];
}
I am sure I am missing something here.
The association in storyboard is correct to my knowledge.
Please correct me where I am going wrong
Thanks for your time
You should not add gesture on self.view.
It should get added on the view for which you want to identify tap event.
You are setting both the Tap Gesture Objects on [self view] object.
Also, the UIImageView object, lets call it imageObj, should have userInteractionEnabled = YES.
instead of:
[[self view] addGestureRecognizer:tapGestureRecognizerImage];
you should do:
[imageObj setUserInteractionEnabled:YES];
[imageObj addGestureRecognizer:tapGestureRecognizerImage];
You generally use -addGestureRecognizer: on the object you want your gesture object to work on.
Say you have a UITapGestureRecognizer object called myTapGesture.
Then, to make it work...
on a UILabel *lblSomeObj it will be:
[lblSomeObj addGestureRecognizer:myTapGesture];
on a UIView *vwSomeObj it will be:
[vwSomeObj addGestureRecognizer:myTapGesture];
etc...
Just add the gesture in the respective views and not in self.view.
UITapGestureRecognizer *tapGestureRecognizerImage = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTapFromImage:)];
[infobutton addGestureRecognizer:tapGestureRecognizerImage];
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTapFrom:)];
[messageOne addGestureRecognizer:tapGestureRecognizer];
Add gesture on UIImageView object and make sure that image view userInteractionEnabled is set to YES
imageObj.userInteractionEnabled = YES;
[imageObj addGestureRecognizer:tapGestureRecognizerImage];
You need to include this piece of code:-
[tapGestureRecognizerImage requireGestureRecognizerToFail:tapGestureRecognizer];
[imageObj addGestureRecognizer:tapGestureRecognizerImage];
I am adding a UITapGestureRecognizer to my application to detect a double tap gesture.
UITapGestureRecognizer *tapgr = [[UITapGestureRecognizer alloc]
initWithTarget:self action:#selector(handleTapGesture:)];
tapgr.numberOfTaps = 2;
tapgr.delegate = self;
[_view addGestureRecognizer:tapgr];
[tapgr release];
This is working fine unless I show a tooltip in my application. They are set up like this:
[_view.toolTipView addTarget:self action:#selector(handleTap:) forControlEvents:UIControlEventTouchUpInside];
Before the introduction of my gesture recognizer, these tooltips where clickable and reacted, now they aren't reacting anymore...
How can I make a gesture recognizer and a standard UIControlEventTouchUpInside-Setup work together?
I found a solution:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
if (gestureRecognizer == tapgr) {
return ![touch.view isKindOfClass:[UIControl class]];
}
return YES;
}
This method prevents the GestureRecognizer from being fired when a UIControl (i.e. Button) is pressed.
if you want the _view can respond single tap and Double tap together, you can try this
UITapGestureRecognizer * singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap:)];
UITapGestureRecognizer * doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleDoubleTap:)];
[doubleTap setNumberOfTapsRequired:2];
//Add this code to disable double tap and only responds to single tap when user tap the _view just once time
[singleTap requireGestureRecognizerToFail:doubleTap];
[self addGestureRecognizer:doubleTap];
[self addGestureRecognizer:singleTap];
[singleTap release];
[doubleTap release];
if you want to use TouchEvent try UIControlEventTouchDownRepeat
[button addTarget:self actionselector(multipleTap:withEvent:) forControlEvents:UIControlEventTouchDownRepeat];
//Try to check wheather is doulbe tap or single tap.
-(IBAction)multipleTap:(id)sender withEvent:(UIEvent*)event {
UITouch* touch = [[event allTouches] anyObject];
if (touch.tapCount == 2) {
// do action.
}
}
You can't do that. I think you should choose another way to implement that.
The following code in my view controller results in an EXC_BAD_ACCESS at the touch.view call:
- (BOOL)handleSingleTap:(UITapGestureRecognizer *)recognizer shouldReceiveTouch:(UITouch *)touch {
if ([touch.view isKindOfClass:[UIControl class]]) { // <<<< EXC_BAD_ACCESS HERE
// we touched a button, slider, or other UIControl
return NO; // ignore the touch
}
[self.view endEditing:YES]; // dismiss the keyboard
return YES; // handle the touch
}
touch appears to be a zombie. Specifically, touch is set to an address, and the debugger thinks that's a UITouch pointer, but it doesn't have any properties:
This did not happen in iOS 4.x. So iOS 6 bug or my fault?
The gesture recognizer is set up as follows (in the ViewController):
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap:shouldReceiveTouch:)];
[tapRecognizer setDelegate:self];
[tapRecognizer setNumberOfTapsRequired:1];
[tapRecognizer setNumberOfTouchesRequired:1];
tapRecognizer.cancelsTouchesInView = NO;
[self.view addGestureRecognizer:tapRecognizer];
[tapRecognizer release];
UPDATE/SOLUTION:
Thanks to Rob for pointing out the user error on my part. Not only that, but I don't actually need the TapRecognizer since I just want to know a touch happened.
Here's the correct code to dismiss the on-screen keyboard with a touch anywhere but in a UIControl:
In the ViewController's viewDidLoad:
UIGestureRecognizer *myRecognizer = [[UIGestureRecognizer alloc] init];
[myRecognizer setDelegate:self];
myRecognizer.cancelsTouchesInView = NO;
[self.view addGestureRecognizer:myRecognizer];
[myRecognizer release];
And:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if ([touch.view isKindOfClass:[UIControl class]]) {
// we touched a button, slider, or other UIControl
return NO; // ignore the touch
}
[self.view endEditing:YES]; // dismiss the keyboard
return YES; // handle the touch
}
You're using a method, handleSingleTap:shouldReceiveTouch:, whose signature does not conform to the permissible signatures. See the Overview section of UIGestureRecognizer Class Reference. It should only have the one parameter, the gesture recognizer.
I think you may be confusing the handler with the UIGestureRecognizerDelegate protocol method gestureRecognizer:shouldReceiveTouch:.