iOS - prevent click on UIButton from propagating to views behind it - ios

I have a button on top of a GLKView.
When i click on the button, i also receive a long touch notification on the GLKView that is behind the button.
How can i prevent the notification from propagating to the view?

Found the solution to my question. The following code will do the trick:
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
UIView* viewRceivingTouch = touch.view;
return (viewRceivingTouch == self.glkView);
}

Related

How to avoid to trigger a UIPanGestureRecognizer when there is a tap on a UIButton?

I have added UIPanGestureRecognizer on the view of my UIViewController.
In this view (a calculator) are some UIButton triggered by the event .TouchUpInside.
The problem comes if I tap on the button and make a small pan at the same time (which might happen if you tap quickly a button and are moving to the next one at the same time). Then the pan gesture is triggered. I would like to avoid it when there is a tap on a button. But I would like to allow it if the tap takes too long (let say 0.3s is enough to trigger the pan gesture).
How can I achieve that?
Set the property cancelsTouchesInView of your gesture recognizer to NO.
Then your button should work properly.
You can use the delegate method like this-
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
if ([touch.view isKindOfClass:[UIButton class]]) {
return NO;
}
return YES;
}
There is a dirty solution. You can just grab it in a UIScrollView

Getting all touch events of an app

I've been looking for a way to intercept all touch events of an app.
I saw that I can add a gesture recognizer to the main window and get all the touches by using it's delegate method:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
NSLog(#"%#",touch);
return NO;
}
This way I don't harm all of the other touch events the app has.
The problem is that I cannot get swipe events and such this way. Moreover, I cannot override UIWindow sendEvents because my app is an outside framework.
I also do not want to add a transparent UIView on top.
Is there any other way to get swipes and other gesturs?
To intercept all touch events of an app, use a UIWindow subclass and override sendEvent:.... Every touch event passes through this bottleneck method. Be warned that you are now operating at a very low level and can easily break everything.
I managed to get this to work by using a custom gesture recognizer attached to the keyWindow and with using the method:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
Which also gives me all the touches and doesn't disturb any other recognizers.
If anyone know why I shouldn't be doing that, I'd be happy to hear about it.

Cancel gesture recognizer on button action

I'm working on an iPad app. I have a view in which the user can draw with his finger. This view has a subview, which is a calculator, and which have buttons.
I would like that when the user touch a button, the superview (in which the user can draw) doesn't take into account this touch. (so the user doesn't draw when he touches the calculator)
Preferably, I would like to not change the code of the calculator view and the code of my superview. I have only access to them via properties of another class.
Is there a way to solve the problem please? I have tried exclusiveTouch, but it doesn't work.
If you have access to the button action and the drawing gesture you can simply set:
gesture.enabled = NO;
To cancel the current gesture processing and / or prevent it from starting. When you want to reenable the gesture depends on what type it is and how it's used but doing it immediately (on the next line) will probably work ok.
Try this but include UIGestureRecognizerDelegate in your header file.
This is from apple "SimpleGestureRecognizers" example-
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
// Disallow recognition of tap gestures in the segmented control.
if ((touch.view == YourButton))
{
//change it to your condition
return NO;
}
return YES;
}

Cancel UIGesture that already started iOS

What I currently have:
I have a child UIViewController inside my main UIViewController. In this child UIViewController I have an UITableView, although anything can be inside of it (UIScrollView, UIImageView, composition of different UIViews sub-classes).
On my main UIViewController I have GestureRecognizerDelegate, so I am receiving callbacks when a gesture is made, like this:
- (void)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer movedWithTouches:(NSSet*)touches andEvent:(UIEvent *)event;
- (void)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer endWithTouches:(NSSet*)touches andEvent:(UIEvent *)event;
#optional
- (void)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer beganWithTouches:(NSSet*)touches andEvent:(UIEvent *)event;
For a particular gesture done on top of the main UIViewController, I am showing a small UIView with an UIImageView. So basically when a touch is made from top to botton where the Y < 100 the UIView animates and shows the image and when I release my finger from the screen the UIView goes back to the top. It's pretty much like the notification center that Apple provides, except that I don't control the scrolling of it and it only shows for 200px from the top.
The problem:
In theory the UIView with the UIImage is the top-est UIView on my hierarchy, but when I keep moving around with my finger on it's area, I can see the UITableView underneath it, moving. For me it doesn't make sense since the UIView with the UIImageView should be capturing those gestures.
What I want:
Simply when I show the UIView with the UIImage disable all the interaction on that childViewController.
What I have tried:
Currently I have this:
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
If I put to NO, the UIView with the UIImage doesn't even show.
If I put something like myChildViewController.view.userInteractionEnabled = NO, since the gesture already started, it won't work. Of course if I use that before the gesture has started everything will work as intended. But the problem is that I won't be able to use the UITableView.
Well, in the end what's working is:
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
// Check if I am not on the Default state (UIView with the UIImageView hidden)
if(currentState != kDefaultState)
{
otherGestureRecognizer.enabled = NO;
[arrayOfDisabledGestures addObject:otherGestureRecognizer];
}
return YES;
}
When I finally close the UIView with the UIImageView I do the following:
[arrayOfDisabledGestures makeObjectsPerformSelector:#selector(setEnabled:) withObject:#YES];
To re-enabled all the gestures. I don't really like the solution, I think there should be (and I bet there are) other ways of doing this.

UITextfield's clear button hides keyboard when its inside UIScrollView

I have a textfield inside a UIScrollView and i want to show a clear button when user starts editing. Also i need to hide keyboard when user taps the background of UIScrollview (but not the textfield). Displaying that clear button isn't a problem, the problem is that when clear button is tapped keyboard gets hidden and the text field doesn't get cleared. Obviously the problem is with the gesture recognizer, because method dealing with this gets fired when the clear button is clicked (but it's not fired when the text field is tapped). Here's my code :
//adding gesture recognizer so i can hide keyboard when user taps scrollview
- (void) textFieldDidBeginEditing:(UITextField *)textField
{
if (self.tapOutside == nil) self.tapOutside = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(textFieldTouchOutSide:)];
[self.scrollView addGestureRecognizer:self.tapOutside];
}
//This hides keyboard BUT IS ALSO CALLED WHEN CLEAR BUTTON IS TAPPED
- (void)textFieldTouchOutSide:(id)sender
{
[self.textfield resignFirstResponder];
}
//NEVER GETS CALLED
- (BOOL) textFieldShouldClear:(UITextField *)textField {
return YES;
}
Any ideas how to solve this? Maybe better way to add gesture recognizer? I can't think of no elegant solution ... Thanks a lot in advance...
I had the same problem and solved it implementing the following method:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
// Disallow recognition of gestures in unwanted elements
if ([touch.view isMemberOfClass:[UIButton class]]) { // The "clear text" icon is a UIButton
return NO;
}
return YES;
}
Don't forget to conform to the "UIGestureRecognizerDelegate" protocol and set the delegate (using your vars):
self.tapOutside.delegate = self;
Cheers
I was just having this issue and this solution worked, however if you do have other buttons on the view that you allow the user to tap while filling out the form you can do the following:
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
// Disallow recognition of gestures in unwanted elements
if ([touch.view isMemberOfClass:[UIButton class]] && [touch.view.superview isMemberOfClass:[UITextField class]]) {
// The "clear text" icon is a UIButton
return NO;
}
return YES;
}
This will narrow down the case to only return No if the button is a subview of a UITextField, as is the case with the clear button, but still hide the keyboard if they touch a normal button that would normally execute your gesture code.

Resources