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.
Related
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);
}
Hi I have a UIView who's alpha is 0.7 and below it are some UITextFields. I don't want it to call touches events while keeping touches events. I tried using
[lightBoxView setUserInteractionEnabled:NO];
But now the UITextFields can become active or first responder. How can I disable it from calling touch events as well as not passing touches to others?
You also need to set the userInteractionEnabled = NO for all the subviews as well.
Try this,
[[lightBoxView subviews] makeObjectsPerformSelector:#selector(setUserInteractionEnabled:)
withObject:[NSNumber numberWithBool:NO]];
This will call setUserInteractionEnabled: on all direct subviews of lightBoxView and set it to NO. For a more complex subview hierarchy you will have to recursively loop through all the child views and disable the user interaction on each one. For this you can write a recursive method in a UIView category. For more details about this category method take a look at this answer.
Hope that helps!
You can remove those control from tough gesture delegate method.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
if ([touch.view isKindOfClass:[UITextFiled class]])
{
return NO;
}
else
{
return YES;
}
}
I have a view with a UIToolbar with a few UIBarButtonItems and a UITableView containing some UITextFields.
I would like to dismiss the keyboard for a textfield with a tap anywhere. Therefore I added a TapGestureRecognizer to the view. To avoid that the TapgestureRecognizer handles taps on the UIBarButtonItems I added the following method (delegate is set).
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
UIView *view = touch.view;
while (view) {
NSLog(#"Class of view: %#", NSStringFromClass([view class]));
view = view.superview;
}
// Disallow recognition of tap gestures in the toolbar
if ([touch.view isKindOfClass:[UIToolbar class]]) {
return NO;
}
if ([touch.view.superview isMemberOfClass:[UIToolbar class]]) {
return NO;
}
return YES;
}
A UIBarButtonItem is not a view itself, but it has UIToolbar as its superview. When I use the above method, the check for isKindOfClass:[UIToolbar class] does not seem to work for all taps on the toolbar. However the check for the superview with isMemberOfClass:[UIToolbar class] works.
I don't understand this. Maybe someone can explain this behavior?
You shouldn't rely on the view hierarchy around private view classes. It could change at any time.
A better approach is to add the gesture to the table view (or other appropriate view which represents the area you're interested in). Just be sure to enable and disable the gesture at appropriate times so as not to block the usual table operation.
I've got a view hierarchy that looks like that
UIScrollView
|
+- UIView
|
+- UITextField
+- UITextField
+- UIButton
What I want is for user which tapped one of the text fields and sees the keyboard on the screen to be able to tap on an "empty space" of UIView to hide keyboard. So, I don't want, for instance, an event from UIButton to bubble up to UIView (that's exactly what happens if I add UITapGestureRecognizer to UIView).
How can I achieve the desired functionality?
In your viewDidLoad method add this gesture recognizer:
UITapGestureRecognizer *gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(dismissKeyboard)];
gestureRecognizer.cancelsTouchesInView = NO;
[self.view addGestureRecognizer:gestureRecognizer];
Then add the dismissKeyboard method:
- (void) dismissKeyboard{
[YOURFIELDHERE resignFirstResponder];
}
You also need to add this to make it so the buttons are still clickable and not overridden by the gesture recognizer:
gestureRecognizer.delegate = self; // in viewDidLoad
<UIGestureRecognizerDelegate> //in your header file
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if ([touch.view isKindOfClass:[UIButton class]]){
return NO;
}
return YES; // handle the touch
}
I encounter this same problem and solve it with a naive solution.
Change the View from an instance of UIView to an instance of UIControl so that it can handle touch events.
Create an IBAction method in the View Controller that handles the touch event. In this case, we will resign any first responder from the view's subviews.
- (IBAction)backgroundTapped:(id)sender
{
[contentView endEditing:YES];
}
contentView is just the instance variable pointing to the View. You can name it anything you want. When you passed the message endEditing to the View, it essentially tells its subviews to resign first responder, thus dismissing the keyboard.
Connect the target (View) and action (IBAction method you just created) via Interface Builder, by opening the connection inspector of the View, select Touch Up Inside and drag it to the File's Owner object, then select the name of the method.
Hopefully it helps.
I know it's a little late, but a quick, simple solution is the following:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
[self.view endEditing:YES];
}
It gets called if you tap on any empty space.
Mike Z's answer is good. But I think the "if condition" below would be easier and simple in UIGestureRecognizerDelegate when you use Mike Z's answer.
Especially when the subviews are not only a button type, they may also be UITableViewCell, Custom View, etc.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
return touch.view == yourEmptySpaceView;
}
I have a simple MapKit app working fine in iOS. It has annotation and when the user clicks on them, the little gray default popup is displayed with the title / subtitle. I even added a UIButton view into it.
So the problem is, I have a search bar above my map. I wanted to resignFirstResponder from the search box whenever the user clicks on the MapView, so I added a simple tap gesture responder. Worked great except now the little gray detail popups no longer show up (only the annotation pins)! I can still tap, zoom, move around etc. Just no popups.
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapped:)];
tap.cancelsTouchesInView = NO;
tap.delaysTouchesBegan = NO;
tap.delaysTouchesEnded = NO;
[mapView addGestureRecognizer:tap];
-(IBAction)tapped:(UITapGestureRecognizer *)geture {
[searchBar resignFirstResponder];
}
Is it possible to have the best of both worlds?
I used a delegate method similar to the following to arbitrate between touches that should go to my custom view's pan gesture recognizer and touches that should go to the scroll view that contained my custom view. Something like it might work for you.
// the following UIGestureRecognizerDelegate method returns YES by default.
// we modify it so that the tap gesture recognizer only returns YES if
// the search bar is first responder; otherwise it returns NO.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
if ((gestureRecognizer == self.tapGestureRecognizer) &&
(gestureRecognizer.view == self.mapView) &&
[searchBar isFirstResponder])
{
return YES; // return YES so that the tapGestureRecognizer can deal with the tap and resign first responder
}
else
{
return NO; // return NO so that the touch is sent up the responder chain for the map view to deal with it
}
}