Im having some issues with adding tapRecognizers to my views.
Im adding an unknown number of events, news and coupons to my UIScrollView where on click i want to open an detail view. However on click the app crashes with following error
Almhults_appen.MainActivity redirectFromHomeScreen:]: unrecognized selector sent to instance 0x7f93c2d4df20
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Almhults_appen.MainActivity redirectFromHomeScreen:]:
As I see it I could make use of an UITableView and add all views to it. However if possible I would personally like to avoid it but since I'm new to Swift I don't trust my judgement here.
if (!event_ar.isEmpty) {
for event: Event in event_ar {
... Init EventView and add to UIScrollView
// Add tapGesture
let tapGesture: UITapGestureRecognizer = UITapGestureRecognizer(
target: self,
action: "redirectFromHomeScreen:"
)
eventView.addGestureRecognizer(tapGesture)
}
}
if (!news_ar.isEmpty) {
... Add news identically to events
}
if (!coupon_ar.isEmpty) {
... Add coupons identically to events
}
Edit added action function
private func redirectFromHomeScreen(sender: UITapGestureRecognizer) -> Void{
... Do stuff
}
Thanks in advance :)
According to your crash report .. first you need to declare funtion
func redirectFromHomeScreen (sender : UITapGestureRecognizer){
}
Declare this and try again
You have to add your gesture to the view you will tap on. And the best moment to add it is within that view's viewDidLoad method. With Objective-C I do that and it works well.
- (void)viewDidLoad
{
[super viewDidLoad];
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(PanToFlipPage:)];
[self.view addGestureRecognizer:pan];
}
Try to add these lines after you have created all of your subviews and buttons.
[self.view setNeedsLayout];
[self.view layoutIfNeeded];
I'm trying to handle a tap event on a segmented control, but when the selected button is clicked again. For example, for the screenshot below where "Second" is already selected, how do I handle the action when the "Second" button is clicked again?
I tried an IBOutlet, but it only triggers when the value has changed. Then I tried the code below, but the same thing where it triggers only when the value changes. In both cases while "Second" is selected, clicking "Second" again does not fire anything. Is there a way to do this?
segmentedControl.addTarget(self, action: "segementedAnyTap:", forControlEvents: .AllEvents)
This works for me, adding a gesture recogniser to the UISegmentedControl
- (void)viewDidLoad
{
[super viewDidLoad];
[self.segmentedControl addTarget:self action:#selector(valueChanged:) forControlEvents:UIControlEventValueChanged];
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(touched:)];
[self.segmentedControl addGestureRecognizer:tapGesture];
}
- (void)valueChanged:(id)sender
{
// value change code
}
- (void)touched:(id)sender
{
// code to check if the segmented controls index has not changed.
// execute desired functionality
}
Great Answer #Sgorbyo, here is a Swift 3 version of it:
override func viewDidLoad() {
super.viewDidLoad()
let segmentedTapGesture = UITapGestureRecognizer(target: self, action: #selector(onTapGestureSegment(_:)))
segmentedControl.addGestureRecognizer(segmentedTapGesture)
}
#IBAction func onTapGestureSegment(_ tapGesture: UITapGestureRecognizer) {
let point = tapGesture.location(in: segmentedControl)
let segmentSize = tipSegmentedControl.bounds.size.width / CGFloat(segmentedControl.numberOfSegments)
let touchedSegment = Int(point.x / segmentSize)
if segmentedControl.selectedSegmentIndex != touchedSegment {
// Normal behaviour the segment changes
segmentedControl.selectedSegmentIndex = touchedSegment
} else {
// Tap on the already selected segment
segmentedControl.selectedSegmentIndex = touchedSegment
}
onSegment(segmentedControl)
}
#IBAction func onSegment(_ sender: Any) {
// Your segment changed selector
}
Add KVO observing.
Exaple:
#pragma mark -
- (void)viewDidLoad {
[_segmentControl addObserver:self forKeyPath:#"selectedSegmentIndex" options:NSKeyValueObservingOptionInitial context:nil];
}
#pragma mark - KVO
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
NSLog(#"segment index: %ld", (long)_segmentControl.selectedSegmentIndex);
}
Result:
2015-06-22 12:31:54.155 Location test[27082:13176230] segment index: 0
2015-06-22 12:31:54.740 Location test[27082:13176230] segment index: 0
2015-06-22 12:31:55.821 Location test[27082:13176230] segment index: 1
2015-06-22 12:31:56.529 Location test[27082:13176230] segment index: 1
I'm not quite sure why are you trying to achieve this but I'd like to suggest subclassing UISegmentedControl and overriding touchesEnded:withEvent:
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesEnded:touches withEvent:event];
[self sendActionsForControlEvents:UIControlEventTouchUpInside];
}
Now your scheduled selector for UIControlEventTouchUpInside will get called every time you press each of the segments and still keep the default functionality of UISegmentedControl.
NOTE: You'll need to handle yourself if that's the first selection of the segment (e.g. keep a private property of the previous value). If you add selector for UIControlEventValueChanged it will also trigger the selector for UIControlEventTouchUpInside and it might cause a bit of confusion or bugs.
Good luck and hope that helps.
I think this could solve the problem:
- (void)viewDidLoad {
[super viewDidLoad];
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(touched:)];
[self.segmentedControl addGestureRecognizer:tapGesture];
}
- (void) valueChanged:(id) sender {
// Your segment changed selector
}
- (void) touched:(UITapGestureRecognizer *) tapGesture {
CGPoint point = [tapGesture locationInView:self.segmentedControl];
NSUInteger segmentSize = self.segmentedControl.bounds.size.width / self.segmentedControl.numberOfSegments;
// Warning: If you are using segments not equally sized, you have to adapt the code in the next line
NSUInteger touchedSegment = point.x / segmentSize;
if (self.segmentedControl.selectedSegmentIndex != touchedSegment) {
// Normal behaviour the segment changes
self.segmentedControl.selectedSegmentIndex = touchedSegment;
} else {
// Tap on the already selected segment, I'm switching to No segment selected just to show the effect
self.segmentedControl.selectedSegmentIndex = UISegmentedControlNoSegment;
}
// You have to call your selector because the UIControlEventValueChanged can't work together with UITapGestureRecognizer
[self valueChanged:self.segmentedControl];
}
I do the following for a segmented control that has 3 options ("categories"). _selectedCategory is a property NSInteger that keeps track of segmented controls currently selected index. On tap, if I find that the _selectedCategory is the same as the one pressed, they're pressing the selected segmented control and I flip it off.
-(IBAction)categorySelected:(id)sender {
if (_selectedCategory == [sender selectedSegmentIndex]) {
sender.selectedSegmentIndex = UISegmentedControlNoSegment;
// update my model, etc...
} else {
_selectedCategory = [sender selectedSegmentIndex];
switch (_selectedCategory) {
case 0:
// do logic...
}
}
}
I encounter a case which I need the segment index before the selection, and I do not want the .valueChanged event stops firing, therefore I come up with this.
Create a subclass for UISegmentedControl and override touchesEnded
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
// Previous selected segment index
let oldIdx = selectedSegmentIndex
super.touchesEnded(touches, with: event)
// New selected segment index
let newIdx = selectedSegmentIndex
// If the previously selected segment index is equal to the new one,
// then you are tapping on the same segment button.
// Call a block, delegate method or whatever to notify this
}
In your comment on your question, you mentioned you're trying to allow users to select 'unread' to display all unread messages, then let them click again to mark all as unread. Instead of using the segment control for that, I'd recommend adding a "Mark all unread" button that appears when the 'unread' segment is selected. That will accomplish the feature you're trying to add while also making it clear to the user that they have a way to mark everything as unread.
You could set your app to display a popover coming from your unread segment with a button to display all as unread. To place the popover, use :
CGRect frame = [segmentControl frame];
frame =CGRectMake((frame.size.width/2*butIndex), 0, frame.size.width/2, segmentControl.bounds.size.height);
[popOver presentPopoverFromRect:frame inView:segmentControl permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
A little late I know, but another technique that worked well for me...
Add a UIButton with a clear background over each segment of the UISegmentedControl. Each UIButton can have its own UIControlEventTouchUpInside event handler-- which can change the UISegmentedControl's selectedSegmentIndex.
Then set the UISegmentedControl.userInteractionEnabled to NO, and remove its UIControlEventValueChanged event handler.
I would like to know how can I send a swipe gesture programmatically without actually swiping the phone. For instance, I have button that in the onClick event I would call swipeGestureRecognizer? Is this possible?
You can call the method you are calling on Swipe, when user taps on button. For examaple, you have a method for swipe gesture, call it onSwipe . Now call onSwipe methos when user taps on the button. Thats it
EDIT
Here is the code for you:
-(void)myMethod{
//Write the logic you want to implement for swipe gesture here.
}
-(IBAction)onClick(UIButton *)sender{
[self myMethod];
}
-(IBAction)onSwipe:(UISwipeGestureRecognizer *)recognizer{
[self myMethod];
}
There might be bugs on the code as I am just typing the code using windows. Correct it for yourself in MAC & edit the answer too. it would definitely work for you
If you need to pass the gesture event to the handler, then
UISwipeGestureRecognizer *gesture = [[UISwipeGestureRecognizer alloc] init];
gesture.direction = UISwipeGestureRecognizerDirectionLeft;
[self handleSwipe:gesture];
I am stumped and I hope someone can help.
I am calling the resign first responder method for all five of my text fields prior to a segue. The segue occurs, if the keyboard was visible prior to the segue, the keyboard remains no matter what I do. This did not happen in IOS6. It is only happening in IOS7.
Thank you so much in advance for your assistance.
Here is the scenario:
The user touches one text field at time to enter data. The keyboard has no problems changing from first responder from one field to the next and can be resigned from the DONE button without issues. The problem comes when the user touches a field that will be populated from the picker view. If the keyboard was visible from one of the previous text fields, it won't go away.
I have this code attempting to resignFirstResponder on the editingDidBegin action of two of the fields. I am using these two fields to hold numbers but I am filling them from a picker on the next view.
- (IBAction)txtRatioOrHullTypeTouched:(id)sender
{
// Hide the keyboard before the segue to the picker occurs.
[self.txtPitch resignFirstResponder];
[self.txtRPM resignFirstResponder];
[self.txtSlipOrSpeed resignFirstResponder];
[self.txtRatio resignFirstResponder];
[self.txtHullType resignFirstResponder];
segueToPicker = YES; // Raise flag indicating that this segue is to the picker.
[self performSegueWithIdentifier:#"toPicker" sender:sender];
}
I also put this same code in the viewWillDisappear as shown here:
- (void)viewWillDisappear:(BOOL)animated // Unchanged
{
// Hide the keyboard before the segue to the picker occurs.
[self.txtPitch resignFirstResponder];
[self.txtRPM resignFirstResponder];
[self.txtSlipOrSpeed resignFirstResponder];
[self.txtRatio resignFirstResponder];
[self.txtHullType resignFirstResponder];
[super viewWillDisappear:animated];
}
Both of these methods are on the initial view, ViewController.m file.
I ended up here removing the text field causing the problem and replacing them with buttons. No scenario I tried (dozens) got this code to work as expected in IOS7, even though it all worked flawlessly in IOS6.
I tried all of the above and it worked as long as i dismissed the controller with a button. The function that was called when pressing the button could call the TextField's resignFirstResponder() function and all was well.
However, when an edge swipe was performed to dismiss the controller the keyboard kept popping up the next time I showed it. In my code I reuse the same controller between views. This might not be wise but, it's snappy!
After trying everything the internet had written (well not really, but pretty close) about this I found that i could implement the TextField's textViewShouldBeginEditing() and return false between the ViewControllers ViewDidDisappear and ViewDidAppear. It's ha hack, but it did the trick when nothing else worked.
I hope this helps you guys!
Swift code:
In my ViewController
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
myTextField.allowEdit = true
}
override func viewDidDisappear(animated: Bool) {
super.viewDidDisappear(animated)
myTextField.allowEdit = false
}
In my TextField class
class MyTextField: UIView, UITextFieldDelegate {
var allowEdit = true
func textFieldShouldBeginEditing(textView: UITextView) -> Bool {
return allowEdit
}
}
You can call endEditing: on the view controller with the text fields. Your viewWillDisappear: method will look like this:
- (void)viewWillDisappear:(BOOL)animated
{
[self.view endEditing:YES];
[super viewWillDisappear:animated];
}
Contributing my 2 cents worth. dismissing keyboard correctly on iOS 9.2, a minimalist sample, FYI.
...
#property (assign) BOOL isTransitioning;
...
-(void)viewWillAppear:(BOOL) animated {
self.isTransitioning = YES;
}
-(void)viewWillDisappear:(BOOL) animated {
self.isTransitioning = YES;
}
-(void)viewDidAppear:(BOOL) animated {
self.isTransitioning = NO;
}
-(void)viewDidDisappear:(BOOL) animated {
self.isTransitioning = NO;
}
-(BOOL) textViewShouldBeginEditing:(UITextView*) tv {
if (self.isTransitioning) {
return NO;
}
return YES;
}
I think due to the way you are leaving the view through a picker, without going through an exit, you need to include the following in your viewController:
- (BOOL) disablesAutomaticKeyboardDismissal
{
return NO;
}
Swift, 2017
override var disablesAutomaticKeyboardDismissal: Bool {
get { return false }
set { }
}
So it seems now that the text field that controls the keyboard will not allow resignation. I used the canResignFirstResponder query on that field and the result (boolean) was FALSE. I also noticed that i get a flashing cursor in the field even after the resignFirstResponder is called. – Larry J Oct 25 '13 at 23:32
I know this is old, but I had a similar issue and wanted to share what worked for me in case it might help anyone else:
After reading the above comment I found that moving [self.view endEditing:YES] from where I had it in textFieldDidBeginEditing to textFieldSHOULDBeginEditing did the trick for me. Now the keyboard is dismissing properly before my segue.
Taking Zaheer's comment into Swift this works very well for me.
view.endEditing(true)
This is a problem i have frequently. My best method to cope is creating a clear button under the keyboard and having that call a dismiss helper. Control the clear button by toggling its isHidden property. Tapping outside the keyboard will hit that clear button and call the dismiss helper. What it won't do is trigger your segue, the user will need to tap again to navigate out but that keyboard will be gone.
in viewDidLoad():
var clearButton: UIButton!
self.clearButton = UIButton(frame: self.view.frame)
self.clearButton.backgroundColor = .clear
self.clearButton.addTarget(self, action: #selector(self.dismissHelper(_:)), for: .touchUpInside)
self.view.addSubview(self.clearButton)
self.clearButton.isHidden = true
Then add the dismiss helper:
func dismissHelper(_ sender: UIButton?) {
self.clearButton.isHidden = true
view.endEditing(true)
}
func displayClearButton(){
print("display clear button, hidden = false")
self.clearButton.isHidden = false
}
then on your textfield add the target
self.textField.addTarget(self, action: #selector(self.displayClearButton), for: .editingDidBegin)
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];
}