iOS - Dismiss keyboard when touching outside of UITextField - ios

I'm wondering how to make the keyboard disappear when the user touches outside of a UITextField.

You'll need to add an UITapGestureRecogniser and assign it to the view, and then call resign first responder on the UITextField on it's selector.
The code:
In viewDidLoad
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(dismissKeyboard)];
[self.view addGestureRecognizer:tap];
In dismissKeyboard:
-(void)dismissKeyboard
{
[aTextField resignFirstResponder];
}
(Where aTextField is the textfield that is responsible for the keyboard)
Swift 3 version looks like that
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.dismissKeyboard (_:)))
self.view.addGestureRecognizer(tapGesture)
For dismissKeyboard
#objc func dismissKeyboard (_ sender: UITapGestureRecognizer) {
aTextField.resignFirstResponder()
}

I mashed up a few answers.
Use an ivar that gets initialized during viewDidLoad:
UIGestureRecognizer *tapper;
- (void)viewDidLoad
{
[super viewDidLoad];
tapper = [[UITapGestureRecognizer alloc]
initWithTarget:self action:#selector(handleSingleTap:)];
tapper.cancelsTouchesInView = NO;
[self.view addGestureRecognizer:tapper];
}
Dismiss what ever is currently editing:
- (void)handleSingleTap:(UITapGestureRecognizer *) sender
{
[self.view endEditing:YES];
}

Check this, this would be the easiest way to do that,
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
[self.view endEditing:YES];// this will do the trick
}
Or
This library will handle including scrollbar auto scrolling, tap space to hide the keyboard, etc...
https://github.com/michaeltyson/TPKeyboardAvoiding

I see that some people are having issues using the UITapGestureRecognizer method. The easiest way that I've accomplished this functionality while still leaving my existing button's tap behavior intact is adding only one line to #Jensen2k 's answer:
[tap setCancelsTouchesInView:NO];
This allowed my existing buttons to still work without using #Dmitry Sitnikov 's method.
Read about that property here (search for CancelsTouchesInView): UIGestureRecognizer Class Reference
I'm not sure how it would work with scrollbars, as I see some had issues with, but hopefully someone else might run into the same scenario I had.

It is better to make your UIView an instance of UIControl (in interface builder) and then connect their TouchUpInside event to dismissKeyboard method. This IBAction method will look like:
- (IBAction)dismissKeyboard:(id)sender {
[aTextBox resignFirstResponder];
}

Swift 4
Setup your UIViewController with this extension method once e.g in viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
self.setupHideKeyboardOnTap()
}
and the keyboard will be dismissed even by tapping on the NavigationBar.
import UIKit
extension UIViewController {
/// Call this once to dismiss open keyboards by tapping anywhere in the view controller
func setupHideKeyboardOnTap() {
self.view.addGestureRecognizer(self.endEditingRecognizer())
self.navigationController?.navigationBar.addGestureRecognizer(self.endEditingRecognizer())
}
/// Dismisses the keyboard from self.view
private func endEditingRecognizer() -> UIGestureRecognizer {
let tap = UITapGestureRecognizer(target: self.view, action: #selector(self.view.endEditing(_:)))
tap.cancelsTouchesInView = false
return tap
}
}

Swift version, this works in combination with other elements (like a UIButton or another UITextField):
override func viewDidLoad() {
super.viewDidLoad()
let tapper = UITapGestureRecognizer(target: self, action:#selector(endEditing))
tapper.cancelsTouchesInView = false
view.addGestureRecognizer(tapper)
}

This is a good generic solution:
Objective-C:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self.view endEditing:YES];
}
Swift:
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
self.view.endEditing(true)
}
Based on #icodebuster solution: https://stackoverflow.com/a/18756253/417652

How about this: I know this is an old post. It might help someone :)
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
NSArray *subviews = [self.view subviews];
for (id objects in subviews) {
if ([objects isKindOfClass:[UITextField class]]) {
UITextField *theTextField = objects;
if ([objects isFirstResponder]) {
[theTextField resignFirstResponder];
}
}
}
}

Swift 4 oneliner
view.addGestureRecognizer(UITapGestureRecognizer(target: view, action: #selector(UIView.endEditing(_:))))

I think the easiest (and best) way to do this is to subclass your global view and use hitTest:withEvent method to listen to any touch. Touches on keyboard aren't registered, so hitTest:withEvent is only called when you touch/scroll/swipe/pinch... somewhere else, then call [self endEditing:YES].
This is better than using touchesBegan because touchesBegan are not called if you click on a button on top of the view. It is better than UITapGestureRecognizer which can't recognize a scrolling gesture for example. It is also better than using a dim screen because in a complexe and dynamic user interface, you can't put dim screen everywhere. Moreover, it doesn't block other actions, you don't need to tap twice to select a button outside (like in the case of a UIPopover).
Also, it's better than calling [textField resignFirstResponder], because you may have many text fields on screen, so this works for all of them.

This must be the easiest way to hide your keyboard by touching outside :
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self.view endEditing:YES];
}
(from How to dismiss keyboard when user tap other area outside textfield?)

If the view is embedded at all in a UIScrollView then you can use the following:
tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag;
tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeInteractive;
The former will animate the keyboard off screen when the table view is scrolled and the later will hide the keyboard like the stock Messages app.
Note that these are are available on iOS 7.0 or above.

You can do this using the Storyboard in XCode 6 and above:
Create the action to hide the keyboard
Add this to the header file of the class used by your ViewController:
#interface TimeDelayViewController : UIViewController <UITextFieldDelegate>
- (IBAction)dissmissKeyboardOnTap:(id)sender;
#end
Then add this to the implementation file of the same ViewController:
- (IBAction)dissmissKeyboardOnTap:(id)sender{
[[self view]endEditing:YES];
}
This will now be one of the 'Received Actions' for your storyboard scene (i.e. ViewController):
Hook up the action to the user event
Now you need to hook up this action to the user gesture of touching off the keyboard.
Important - You need to convert the 'UIView' that's contained in your storyboard to a UIControl, so it can receive events. Select the view from your View Controller Scene hierarchy:
...and change its class:
Now drag from the small circle next to the 'received action' for your scene, onto an 'empty' part of your scene (actually you're dragging the 'Received Action' to the UIControl). You'll be shown a selection of events that you can hook up your action to:
Select the 'touch up inside' option. You've now hooked the IBAction you created to a user action of touching off the keyboard. When the user taps off the keyboard, it will now be hidden.
(NOTE: To hook the action to the event, you can also drag from the received action directly onto the UIControl in your View Controllers hierarchy. It's displayed as 'Control' in the hierarchy.)

If I got you right you want to resign keyboard wile tapping on outSide of textfield but you don't have reference of your textfield.
Try this;
Take global textField, lets call it reftextField
Now in textFieldDidBeginEditing set referenced text field to
- (void) textFieldDidBeginEditing:(UITextField *)textField{
reftextField = textField;
}
Now you can happily use on any button clock, (adding a transparent button on begin editing recomended)
- (void)dismissKeyboard {
[reftextField resignFirstResponder];
}
Or for resigning done button try this.
//for resigning on done button
- (BOOL) textFieldShouldReturn:(UITextField *)textField{
[textField resignFirstResponder];
return YES;
}

Just to add to the list here my version of how to dismiss a keyboard on outside touch.
viewDidLoad:
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap:)];
[self.view addGestureRecognizer:singleTap];
Anywhere:
-(void)handleSingleTap:(UITapGestureRecognizer *)sender{
[textFieldName resignFirstResponder];
puts("Dismissed the keyboard");
}

In swift 5 You can use following code to dismiss keyboard outside textfield
override func viewDidLoad() {
// ... code
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.dismissKeyboard(_:)))
self.view.addGestureRecognizer(tapGesture)
}
#objc func dismissKeyboard(_ sender: UITapGestureRecognizer) {
self.view.endEditing(true)
}

Objective-C:
Add this code in your ViewController.m file :
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self.view endEditing:YES];
}
Swift:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
view.endEditing(true)
}

Plenty of great answers here about using UITapGestureRecognizer--all of which break UITextField's clear (X) button. The solution is to suppress the gesture recognizer via its delegate:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
BOOL touchViewIsButton = [touch.view isKindOfClass:[UIButton class]];
BOOL touchSuperviewIsTextField = [[touch.view superview] isKindOfClass:[UITextField class]];
return !(touchViewIsButton && touchSuperviewIsTextField);
}
It's not the most robust solution but it works for me.

You can create category for the UiView and override the touchesBegan meathod as follows.
It is working fine for me.And it is centralize solution for this problem.
#import "UIView+Keyboard.h"
#implementation UIView(Keyboard)
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self.window endEditing:true];
[super touchesBegan:touches withEvent:event];
}
#end

Swift version of #Jensen2k's answer:
let gestureRecognizer : UITapGestureRecognizer = UITapGestureRecognizer.init(target: self, action: "dismissKeyboard")
self.view.addGestureRecognizer(gestureRecognizer)
func dismissKeyboard() {
aTextField.resignFirstResponder()
}
One liner
self.view.addTapGesture(UITapGestureRecognizer.init(target: self, action: "endEditing:"))

I used Barry example for my new development. It worked great! but i had to include a slightly change, required to dismiss the keyboard only for the textfield being edited.
So, I added to Barry example the following:
- (void) textFieldDidBeginEditing:(UITextField *)textField
{
_textBeingEdited = textField;
}
-(void) textFieldDidEndEditing:(UITextField *)textField
{
_textBeingEdited = nil;
}
Also, I changed hideKeyboard method as follows:
- (IBAction)hideKeyboard:(id)sender
{
// Just call resignFirstResponder on all UITextFields and UITextViews in this VC
// Why? Because it works and checking which one was last active gets messy.
//UITextField * tf = (UITextField *) sender;
[_textBeingEdited resignFirstResponder];
}

One of the most easiest and shortest way is to add this code to your viewDidLoad
[self.view addGestureRecognizer:[[UITapGestureRecognizer alloc]
initWithTarget:self.view
action:#selector(endEditing:)]];

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
if let touch = touches.first{
view.endEditing(true)
}
}

I tried many of the responses here and had no luck. My tap gesture recognizer was always causing my UIButtons to not respond when tapped, even when I set the cancelsTouchesInView property of the gesture recognizer to NO.
This is what eventually solved the issue:
Have an ivar:
UITapGestureRecognizer *_keyboardDismissGestureRecognizer;
When a text field begins editing, set the gesture recognizer:
- (void) textFieldDidBeginEditing:(UITextField *)textField
{
if(_keyboardDismissGestureRecognizer == nil)
{
_keyboardDismissGestureRecognizer = [[[UITapGestureRecognizer alloc]
initWithTarget:self
action:#selector(dismissKeyboard)] autorelease];
_keyboardDismissGestureRecognizer.cancelsTouchesInView = NO;
[self.view addGestureRecognizer:_keyboardDismissGestureRecognizer];
}
}
Then the trick is in how you set up the dismissKeyboard method:
- (void) dismissKeyboard
{
[self performSelector:#selector(dismissKeyboardSelector) withObject:nil afterDelay:0.01];
}
- (void) dismissKeyboardSelector
{
[self.view endEditing:YES];
[self.view removeGestureRecognizer:_keyboardDismissGestureRecognizer];
_keyboardDismissGestureRecognizer = nil;
}
I guess there's just something about getting the dismissKeyboardSelector execution out of the touch handling execution stack...

Send message resignFirstResponder to the textfiled that put it there. Please see this post for more information.

This works
In this example, aTextField is the only UITextField.... If there are others or UITextViews, there's a tiny bit more to do.
// YourViewController.h
// ...
#interface YourViewController : UIViewController /* some subclass of UIViewController */ <UITextFieldDelegate> // <-- add this protocol
// ...
#end
// YourViewController.m
#interface YourViewController ()
#property (nonatomic, strong, readonly) UITapGestureRecognizer *singleTapRecognizer;
#end
// ...
#implementation
#synthesize singleTapRecognizer = _singleTapRecognizer;
// ...
- (void)viewDidLoad
{
[super viewDidLoad];
// your other init code here
[self.view addGestureRecognizer:self.singleTapRecognizer];
{
- (UITapGestureRecognizer *)singleTapRecognizer
{
if (nil == _singleTapRecognizer) {
_singleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(singleTapToDismissKeyboard:)];
_singleTapRecognizer.cancelsTouchesInView = NO; // absolutely required, otherwise "tap" eats events.
}
return _singleTapRecognizer;
}
// Something inside this VC's view was tapped (except the navbar/toolbar)
- (void)singleTapToDismissKeyboard:(UITapGestureRecognizer *)sender
{
NSLog(#"singleTap");
[self hideKeyboard:sender];
}
// When the "Return" key is pressed on the on-screen keyboard, hide the keyboard.
// for protocol UITextFieldDelegate
- (BOOL)textFieldShouldReturn:(UITextField*)textField
{
NSLog(#"Return pressed");
[self hideKeyboard:textField];
return YES;
}
- (IBAction)hideKeyboard:(id)sender
{
// Just call resignFirstResponder on all UITextFields and UITextViews in this VC
// Why? Because it works and checking which one was last active gets messy.
[aTextField resignFirstResponder];
NSLog(#"keyboard hidden");
}

- (void)viewDidLoad
{
[super viewDidLoad];
UITapGestureRecognizer *singleTapGestureRecognizer = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:#selector(handleSingleTap:)];
[singleTapGestureRecognizer setNumberOfTapsRequired:1];
[singleTapGestureRecognizer requireGestureRecognizerToFail:singleTapGestureRecognizer];
[self.view addGestureRecognizer:singleTapGestureRecognizer];
}
- (void)handleSingleTap:(UITapGestureRecognizer *)recognizer
{
[self.view endEditing:YES];
[textField resignFirstResponder];
[scrollView setContentOffset:CGPointMake(0, -40) animated:YES];
}

In this case, there can be use ScrollView and added to TextField in ScrollView and I want Tap the ScrollView and View then Dismiss the Keyboard. I tried to create sample code just in case. Like this,
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var textField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(ViewController.tap(_:)))
view.addGestureRecognizer(tapGesture)
// Do any additional setup after loading the view, typically from a nib.
}
func tap(gesture: UITapGestureRecognizer) {
textField.resignFirstResponder()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Your Storyboard Look at that Just Like.

You can use UITapGestureRecongnizer method for dismissing keyboard by clicking outside of UITextField. By using this method whenever user will click outside of UITextField then keyboard will get dismiss. Below is the code snippet for using it.
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:#selector(dismissk)];
[self.view addGestureRecognizer:tap];
//Method
- (void) dismissk
{
[abctextfield resignFirstResponder];
[deftextfield resignFirstResponder];
}

Related

How to remove keyboard from textview when uiview is clicked?

I am trying to remove the keyboard when it is in editing mode of a textview.I have added tap gesture on the main view.But on the click of when editing mode is for textfield then keyboard is removed but when editing mode is for textview then keyboard is not removed.Please tell me how can i tackle this issue?
added tap gesture to the main view.
UITapGestureRecognizer *singleFingerTap =
[[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(handleSingleTap:)];
[self.main_view setUserInteractionEnabled:true];
[self.main_view addGestureRecognizer:singleFingerTap];
calling method
- (void)handleSingleTap:(UITapGestureRecognizer *)recognizer
{
if([self.txt_username isFirstResponder])
{
[self.txt_username resignFirstResponder];
}
if([self.txt_password isFirstResponder])
{
[self.txt_password resignFirstResponder];
}
}
I have already set the delegate for text view & also i have added the textview protocol.
delete code for set setUserInteractionEnabled =true , u not need that
and add this in your func
-(void)handleSingleTap:(UITapGestureRecognizer *)recognizer
{
[self.view endEditing:YES];
}
or u can use library TPKeyboardAvoiding , set for scrollview..its automatic close keyboard when u tap view..so u not need that UITapGestureRecognizer
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
[[self view] endEditing:YES];
}
When there are new or changed touches for a given phase, the app object calls one of these methods. Each method takes two parameters: a set of touches and an event.
For more info Click here
i think you just put only textview resingFirstresponder.
- (void)handleSingleTap:(UITapGestureRecognizer *)recognizer
{
[self.txt_username resignFirstResponder];
}
use it.

How can I detect the active textfield is touched again?

Textfield containes a data picker. And because picker has no Return button, I have to close inputView somehow. I do not want to use inputAccessoryView so I though if user press again textfield should dismiss textfield. But I do not know how detect touch event. I subclassed UITextField and overwritten touchesBegan method, but it never get called even dow if I set userInteractionEnabled to false.
I tried to set up a tap gesture recognizer, first it fires, but second times not.
class TextFieldCloseWhenTouched: UITextField {
override func didMoveToWindow() {
self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "tapped"))
}
func tapped() {
if self.isFirstResponder() {
self.resignFirstResponder()
} else {
self.becomeFirstResponder()
}
}
}
You can achieve this using the delegate function (BOOL)textFieldShouldBeginEditing:(UITextField *)textField without subclassing textfield, but then you will need to handle adding and removing the picker to the view with animation yourself, not as input view.
-(BOOL)textFieldShouldBeginEditing:(UITextField *)textField{
if(isTextFieldOpen){
[self dismissDataPicker];
}
else{
[self showDataPicker];
}
isTextFieldOpen=!isTextFieldOpen;
return NO;
}
However, I don't think that this is the best user experience to make. I think it's better to either add the data picker to UIView with toolbar that has Done/Cancel button(s), or add a tap gesture recognizer to the main view when the data picker is shown so when the user tap anywhere it's dismissed.
If this is the only textfield in your screen, or you are okay to dismiss all by tapping anywhere, you can just add the following to your viewcontroller
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
[super touchesBegan:touches withEvent:event];
[self.view endEditing:YES];
}

Hide keyboard on touch anywhere outside UITextField when view includes UICollectionView

There are some answers out there such as this, but in the case where there is a UIScrollView or UICollectionView present, it doesn't work.
The touchesBegan method on the viewController will never get called.
On screen, I have a UITextField at the top.
Below that, filling up the rest of the screen is a UICollectionView.
I need to dismiss the keyboard if I touch anywhere besides the UITextField (including the collection view obviously)
So what is the best way to do this?
For such a common UI paradigm it seems like there should be a well-known solution, but I've yet to come across it.
To dismiss Keyboard on tap of the View: Add a Tap gesture to your ViewController.collectionView as follows:
//declare a property to store your current responder
#property (nonatomic, assign) id currentResponder;
//in viewDidLoad:
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(resignOnTap:)];
[singleTap setNumberOfTapsRequired:1];
[singleTap setNumberOfTouchesRequired:1];
[self.collectionView addGestureRecognizer:singleTap];
//Implement the below delegate method:
- (void)textFieldDidBeginEditing:(UITextField *)textField {
self.currentResponder = textField;
}
//Implement resignOnTap:
- (void)resignOnTap:(id)sender {
[self.currentResponder resignFirstResponder];
}
The simple way to do it is:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self.view endEditing:YES];
}
Here's a better solution which doesn't require adding gesture recognisers individually to everything. It's in Swift, but could easily be converted to ObjC.
Add the following to your viewDidLoad():
let tap = UITapGestureRecognizer(target: self, action: "dismissKeyboard")
tap.cancelsTouchesInView = false
view.addGestureRecognizer(tap)
and then add the following method declaration:
func dismissKeyboard()
{
view.endEditing(true)
}
...where view is your text field.

Custom UIControl and Gesture Recognizer

I am trying to create a custom UIControl similar to a slider.
This control is to be the subview of a view that also has a tap gesture recognizer attached to it.
The problem now is that this tap gesture recognizer cancels the touches sent to my control. Is there a way I can override this from within the code of my control?
If I look into the standard controls in iOS it looks as if UIButton has a way of overriding the tap gesture recognizer but UISlider doesn't. So if I replace my custom control with a UIButton the tap gesture recognizer does not trigger its action, but if I replace it with a slider it does.
edit: I made a small project in Xcode to play around in. Download here https://dl.dropboxusercontent.com/u/165243/TouchConcept.zip and try to change it so that
The UICustomControl does not know about the tap gesture recognizer
The UICustomControl is not cancelled when the user taps down on the yellow box
The UICustomControl does not inherit from UIButton (that is a solution that does not feel right and might give me more headaches later on)
The code:
// inherit from UIButton will give the wanted behavior, inherit from UIView (or UIControl) gives
// touchesCancelled by the gesture recognizer
#interface UICustomControl : UIView
#end
#implementation UICustomControl
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{ NSLog(#"touchesBegan"); }
-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{ NSLog(#"touchesMoved"); }
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{ NSLog(#"touchesEnded"); }
-(void) touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{ NSLog(#"touchesCancelled"); }
#end
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(logTap:)];
[self.view addGestureRecognizer:tapRecognizer];
UIView *interceptingView = [[UICustomControl alloc]initWithFrame:CGRectMake(10, 10, 100, 100)];
interceptingView.userInteractionEnabled = YES;
interceptingView.backgroundColor = [UIColor yellowColor];
[self.view addSubview: interceptingView];
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void) logTap: (id) sender
{
NSLog(#"gesture recognizer fired");
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
You can configure the gesture recognizer to not cancel touches in the view it's attached using the "cancels touches in view" property:
myGestureRecognizer.cancelsTouchesInView = NO;
I'm a little bit late, but for those (like me) stumbling into this question, I used an alternative solution:
Use the delegate of the gesture recogniser.
UITapGestureRecognizer *tapGestRec = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(dismissInfoBox:)];
tapGestRec.delegate = self;
[self.view addGestureRecognizer:tapGestRec];
Then do a sort of hit test in the shouldReceiveTouch delegate function when the gesture recogniser wants to handle/swallow a touch.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
CGPoint location = [touch locationInView:self.view];
return !CGRectContainsPoint(self.myCustomControl.frame, location) && !CGRectContainsPoint(self.myOtherCustomControl.frame, location);
}
I did all this in my ViewController, this way the UIControl does not have to know about any sibling views and the gesture recogniser does not 'steal' taps from my custom controls and only handles 'uncaught' taps.
Also, this way you won't trigger both the gesture recogniser and the custom control, which would happen with cancelsTouchesInView.
BTW, maybe it works with UIButton because UIButton uses gesture recognisers internally? I think they understand each other, while UIControls and recognisers do not. Not sure though.
override gestureRecognizerShouldBegin(_:) in your UIControl subclass.
public override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
if gestureRecognizer.isKind(of: UITapGestureRecognizer.self) {
return false
} else {
return super.gestureRecognizerShouldBegin(gestureRecognizer)
}
}
You should follow this tutorial
http://www.raywenderlich.com/1768/uiview-tutorial-for-ios-how-to-make-a-custom-uiview-in-ios-5-a-5-star-rating-view
It shows you How to make a custom uiview.
And then do follow this one
http://www.raywenderlich.com/29474/ipad-for-iphone-developers-101-in-ios-6-custom-input-view-tutorial

How to block a gesture from superview to subview?

I'm writing a module that everytime I swipe on a view, two sub views with a half size of the view will be added. Those subviews have their own gestures (eg: pan,...). The first time I swipe, it's OK because none of subview has been created. But once the subview been created, everytime I swipe, the swipe gesture is alway pass to its subviews. :(, so I have to swipe 2 times to divide.
I want to know is there any way to block swipe passing to its subview? Thank you.
UPDATE
I used shouldRecognizeSimultaneouslyWithGestureRecognizer to make those gestures work simultaneously. But there's still have some problems. The parent view have its Swipe gesture, the subview have its Pan gesture. Since I use souldRecognizeSimultaneouslyWithGestureRecognizer, sometime when I'm panning, the swipe gesture triggers. So, you know how to disable Swipe while Pan is active in this situation?
You have to implement the UIGestureRecognizerDelegate method:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer;
And add your controller as the delegate of the gesture recognizers. Then, when two gesture recognizers respond to a gesture, this method will be called and here you can implement the logic you want for your app.
In the interface declaration of the controller you have to type:
#interface testcViewController () <UIGestureRecognizerDelegate>
Then, when creating the gesture recognizer:
UISwipeGestureRecognizer *swipe = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(swipe)];
swipe.direction = UISwipeGestureRecognizerDirectionDown;
swipe.delegate = self;
[self.view addGestureRecognizer:swipe];
And then, finally, you add this method to the controller:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
BOOL shouldInteract = NO;
//Here you decide whether or not the two recognizers whould interact.
return shouldInteract;
}
EDIT
You can also implement
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer;
And here, detect if you have already presented the subviews, and block any gesture you want.
To block all gesture recognizers from superviews I created a UIGestureRecognizer sub class that will do just that when attached to a view. See the following code (taken from my WEPopover project):
#import "WEBlockingGestureRecognizer.h"
#import <UIKit/UIGestureRecognizerSubclass.h>
#implementation WEBlockingGestureRecognizer
- (id)init {
return [self initWithTarget:self action:#selector(__dummyAction)];
}
- (id)initWithTarget:(id)target action:(SEL)action {
if ((self = [super initWithTarget:target action:action])) {
self.cancelsTouchesInView = NO;
}
return self;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
if (self.state == UIGestureRecognizerStatePossible) {
self.state = UIGestureRecognizerStateBegan;
}
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
self.state = UIGestureRecognizerStateRecognized;
}
- (BOOL)canBePreventedByGestureRecognizer:(UIGestureRecognizer *)preventingGestureRecognizer {
return [self isGestureRecognizerAllowed:preventingGestureRecognizer];
}
- (BOOL)canPreventGestureRecognizer:(UIGestureRecognizer *)preventedGestureRecognizer {
return ![self isGestureRecognizerAllowed:preventedGestureRecognizer];
}
- (BOOL)shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return ![self isGestureRecognizerAllowed:otherGestureRecognizer];
}
- (BOOL)shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return NO;
}
- (BOOL)isGestureRecognizerAllowed:(UIGestureRecognizer *)gr {
return [gr.view isDescendantOfView:self.view];
}
- (void)__dummyAction {
}
#end
If you want to block all gesture recognizers attached to parent views of some view, just do the following:
- (void)blockParentGesturesForView:(UIView *)v {
[v addGestureRecognizer:[WEBlockingGestureRecognizer new]];
}
set userinteractionEnabled to NO of your subView
subview.userinteractionEnabled=NO
if you dont want to disable userInteraction then use cancelsTouchesInView method
cancelsTouchesInView—If a gesture recognizer recognizes its gesture,
it unbinds the remaining touches of that gesture from their view (so
the window won’t deliver them). The window cancels the previously
delivered touches with a (touchesCancelled:withEvent:) message. If a
gesture recognizer doesn’t recognize its gesture, the view receives
all touches in the multi-touch sequence.
try like this,
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
return NO;
}
Considering that I have a dialogView as a direct subview of my UIViewController's main view I'm attaching a gesture recognizer to the main view and do the following (setting my view controller as the gesture recognizer delegate):
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
let point = touch.location(in: view)
return !dialogView.frame.contains(point)
}
Took what I read here and made a Swift 5 that works great (for me)! I have a slide control in a view that also has a swipe recognizer for moving to another view. If the finger doesn't hit the slider's thumb, then the whole view moves.
By placing this view under the slider (expanded so its somewhat bigger), misses don't do anything.
final class BlockingView: UIView, UIGestureRecognizerDelegate {
let swipe: UISwipeGestureRecognizer = UISwipeGestureRecognizer();
init() {
super.init(frame: CGRect.zero)
swipe.direction = [.left, .right]
self.addGestureRecognizer(swipe)
}
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
#objc override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { return false }
}
Hope this saves someone the trouble!

Resources