I have a UITableView with an associated UITableViewController. However, I've modified the table to also have a view with a textfield subview.
As always, I want the keyboard to disappear when the user hits 'done' (easy) and when they touch anywhere else on screen other than the textfield (hard, stuck!).
The normal way to achieve this is to change the class to UIControl so it can handle actions... but I can't do this for my UITableView/UITableViewController combination.
How can I solve this problem?
U can handle user touches by adding a UITapGestureRecognizer to your view.
For example if u don't want to enable row selection in your tableView u call self.tableView.allowsSelection = NO;
But if u still want to detect user touches u add a UITapGestureRecognizer to your tableView (or to tableView.superview).
U can have more control if u implement the UIGestureRecognizerDelegate, this way u can detect and then choose witch touches to receive and witch not.
To do that just add this code to your UITableViewController:
- (void)viewDidLoad
{
[super viewDidLoad];
self.tableView.allowsSelection = NO;
UITapGestureRecognizer *tgr = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(viewTapped:)];
tgr.delegate = self;
[self.tableView addGestureRecognizer:tgr]; // or [self.view addGestureRecognizer:tgr];
[tgr release];
}
- (void)viewTapped:(UITapGestureRecognizer *)tgr
{
NSLog(#"view tapped");
// remove keyboard
}
// this is optional, it let u choose witch touches to receive, for example here I'm checking if user has tapped on a textField
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
if ([touch.view isKindOfClass:[UITextField class]]) {
NSLog(#"User tapped on UITextField");
}
return YES; // do whatever u want here
}
A normal practice is to put a custom UIButton( it becomes visible only when uitextfield begins editing ) behind keyboard view, and when user clicks on screen he actually clicks on that button, and associated selector can resign first responder.
-(void) closeKeyboard:(UIButton *) b {
[self.view endEditing:YES]; //assuming self is your top view controller.
[b setHidden:YES];
}
Using endEditing is better, cause it loops through all subviews and looks for current first responder.
Using alloc breaks with ARC enabled
Just add the following to the viewController
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
//where text field is a #property (nonatomic,retain) IBOutlet to your textfield
[textField resignFirstResponder];
}
When a row is tapped the didSelectRowAtIndexPath is called. If a textField located inside inside a row is tapped then the textfield delegate is called.
So in addition to your done button method, in didSelectRowAtIndexPath add a check for the text field being first responder and ask it to resign. Assuming a the selected indexPath is not the row of the textfield.
Related
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.
I have a situation in which I need to distinguish between a user tapping a text field and a text field gaining focus programmatically. In this setting, text fields can gain focus programmatically, or through user interaction. Therefore, textShouldBeginEditing is not appropriate since it is called regardless of whether the event is user interaction or programmatic.
My text fields are in a table view and I am trying to set the gesture recognizers in cellForRowAtIndexPath. Here is my code from cellForRowAtIndexPath:
if([cell isKindOfClass:[PPTTextCell class]]){
UITapGestureRecognizer *textTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(textTapped:)];
UITextField* textField = (UITextField*)[self getInputControlFromCell:cell];
[textField setUserInteractionEnabled:YES];
[textField.superview addGestureRecognizer:textTap];
}
The method getInputControlFromCellreturns a text field if one exists in the cell. In the same class I have the following:
- (void) textTapped:(UITapGestureRecognizer*)tap {
NSLog(#"text field tappped");
}
When tapping the text field, the textTapped method is never fired. What am I missing? Thanks!
UITextField does not send touch events, but you can make a subclass and override pointInside: withEvent: method.
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
NSLog(#"text field tappped");
return YES;
}
I have a button, it works as usual on iOS 5 and 6. But on iOS 7 when I press the button the keyboard dismisses, but the method is not called. When I press it second time it works as intended.
Why is that?
Here is the code:
[self.loginButton addTarget:self action:#selector(loginButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
I have the button in UITableView cell.
EDIT:
Here is how I dismiss keyboard at the beginning of this method, but this method is not getting called o iOS7 until the second time I tap on the button.
for (UITextField *field in #[self.loginField, self.passwordField]) {
if ([field isFirstResponder]) {
[field resignFirstResponder];
}
}
I also have gesture recognizer to remove keyboard on tap outside:
UITapGestureRecognizer *tapper = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(removeKeyboard)];
[self.view addGestureRecognizer:tapper];
tapper.cancelsTouchesInView = NO;
Here is its method:
- (void) removeKeyboard
{
[self traverseAllSubviewsOfView:self.view withBlock:^(UIView *inView) {
[inView resignFirstResponder];
}];
}
Here is what helped me - I set controller as delegate for gesture recognizer and implement following method:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
// test if touch is on button
if ([touch.view isKindOfClass:[UIControl class]]) {
return NO;
}
return YES; // handle the touch
}
Did you check the cancelsTouchesInView property of the gesture recognizer? This property controls if touches are delivered to any underlying views. So settings this to NO should allow the touch to be sent to your button. More detail here.
This property can be set in code or in Interface Builder. You can set it in IB by highlighting the gesture recognizer and uncheck "Cancels Touches in View" (I think) in the Attributes Inspector.
I'm not in front of a Mac currently so I can't confirm the exact wording.
Is it possible to dismiss the keyboard when you have MULTIPLE UITextFields ? If so how ?
As a side note, do I have to dismiss the keyboard for Each and Every field or can it be done globally ? Oh and it would be super cool if I don't have to touch the DONE button, I'd ideally like a solution that where the user touches anything BUT the field in question and the keyboard automagically disappears...
Oh and if you'd be so kind step by step instructions.
I should have added that I have a method already to resign the keyboard....
However, it only runs when my form is submitted! (see method below)
My question is how to the keyboard to hide/dismiss without having to jump thru so many damned hoops! You'd figure after 6 years, a mature operating system would have a way to GLOBALLY hide the keyboard....NOT!
Ok, enough whining....
- (void)hideKeyboard {
[self.dancePlace resignFirstResponder];
[self.danceGate resignFirstResponder];
[self.danceTerminal resignFirstResponder];
[self.danceText resignFirstResponder];
[self.danceDate resignFirstResponder];
[self.danceStyle resignFirstResponder];
[self.danceTimeOut resignFirstResponder];
}
And this is called when my button is submitted....
- (IBAction)addListingPressed:(id)sender {
// NSLog(#"BUTTON PRESSED");
[self hideKeyboard];
[self valuesAdded];
}
My question, assuming anyone can answer this...and I suspect not, is there a way to globally hide the keyboard if the following conditions are MET: 1.) the user taps OUT of any one of the existing fields, 2.) presses anywhere else on the screen. 3.) Is no more than a line or two in the existing viewcontroller.m file. 4.) I don't have to add a confusing button on the viewcontroller. (any time I have to add outlets, the damned thing is crashing on me...and then nastiness happens, and really...remember I am JUST a beginner, and its very confusing to read that I have to place this here and that there...oy. Simple folks, simple. I'm not looking for elegant solution, just so that it works.
I have a super class that all my view controllers inherit from. In that class I have this code.
MySuperViewController.h
#import <UIKit/UIKit.h>
#interface MySuperViewController : UIViewController
#property(strong, nonatomic) UITapGestureRecognizer *backgroundTapGestureRecognizer;
#end
MySuperViewController.m
- (void)viewDidLoad{
//add a tap gesture recognizer to capture all tap events
//this will include tap events when a user clicks off of a textfield
self.backgroundTapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(onBackgroundTap:)];
self.backgroundTapGestureRecognizer.numberOfTapsRequired = 1;
self.backgroundTapGestureRecognizer.cancelsTouchesInView = NO;
[self.view addGestureRecognizer:self.backgroundTapGestureRecognizer];
}
- (void)onBackgroundTap:(id)sender{
//when the tap gesture recognizer gets an event, it calls endEditing on the view controller's view
//this should dismiss the keyboard
[[self view] endEditing:YES];
}
I have the UITapGestureRecognizer as a public property, so I can override it if I need to.
subclass
MyViewController.h
#import <UIKit/UIKit.h>
#import "MySuperViewController.h"
#interface MyViewController : MySuperViewController<UIGestureRecognizerDelegate>
#end
MyViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
//You don't always want the keyboard to be dismissed, so you tie into the gesture recognizer's delegate method
//By doing this, you can stop the endEditing call from being made
[self.backgroundTapGestureRecognizer setDelegate:self];
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
//touch.view is the view that recieved the touch
//if this view is another textfield or maybe a button, you can return NO and the endEditing call won't be made
if (touch.view == self.myViewThatShouldNotBeBlocked) {
return NO;
}
//if you want the gesture recognizer to accept the event, return yest
return YES;
}
I uploaded an example project to github.
https://github.com/JeffRegan/KeyboardBeGone
RDVKeyboardAvoiding is a scroll view with a tap gesture recognizer, designed for multiple textViews/textFields. It keeps track of the active view and removes a lot of boilerplate code.
tap anywhere outside the textField .. it will hide it..
[self.view endEditing:YES];
There are couple of other ways to do it.
[myEditField resignFirstResponder];
[myEditField endEditing];
[parentView endEditing];
If you dont wont to do so many things and simply want to dismiss keyboard than give iboutlet to each of your text filed to following method..
-(IBAction)hidekeyboard:(id)sender
{
[sender resignFirstResponder];
}
Yes, you only have to dismiss it for the one that is currently being edited.
In order to know which one is being edited, you can check the -(BOOL)isFirstResponder property, which will return YES if it is the first responder (the one being edited) or NO if it is not. Once you know which one is the first responder you can call -(void)resignFirstResponder on that one to get rid of the keyboard.
For example, if you have a method called -(void)aMethod that you want to dismiss the current view controller and you have an array of textViews called textArray, you could do a little loop such as:
-(void)aMethod {
for (UITextField *text in self.textArray) {
if ([text isFirstResponder]) [text resignFirstResponder];
return;
}
}
This way, you can have a variable number of textFields and it will still work.
If you only have one or two textFields and you do not want to create an Array object, you could do (assuming the fields are named text1 and text2:
-(void)aMethod {
if ([text1 isFirstResponder]) [text1 resignFirstResponder];
else if([text2 isFirstResponder]) [text2 resignFirstResponder];
}
Also, to make things easier for the future you could create a category method for UIView (which is what I do) to get the current first responder if it exists as a subview of that view:
#implementation UIView (GetFirstResponder)
- (UIView *)getFirstResponder {
if ([self isFirstResponder]) return self;
else {
for (UIView *subview in self.subviews) {
UIView *firstResponder = [subview getFirstResponder];
if (firstResponder) return firstResponder;
}
}
return nil;
}
You can put this method on the top of any file that you want to call it from, or create a separate file for it and import it.
Once you have this method, you can call:
- (void)aMethod {
UIView *view = [self.view getFirstResponder];
if (view) [view resignFirstResponder];
}
[superview endEditing:YES]; // superview can be the view controller's view property.
I have set the delegate of my textfield to self and I have added the delegate for it in the .h, but I have a problem. I want the keyboard to hide If I click anything but the textfield in the view. Is this possible? If so, how would I do it?
I found a simpler way to hide the keyboard and it works when you click on the screen way.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
// endEditing: This method looks at the current view and its subview hierarchy for the text field that is currently the first responder.
// If it finds one, it asks that text field to resign as first responder
[[self view] endEditing:TRUE];
}
You can do some "hacking"... Like this:
- (void)textFieldDidBeginEditing:(UITextField *)textField{
//glass is a class's property
if(glass){
self.glass=nil;
}
glass=[[UIButton alloc] initWithFrame:CGRectMake(0, 0, 320, 480)]; //put the size you want.
[glass addTarget:self action:#selector(hideGlass) forControlEvents:UIControlEventTouchUpInside];
[self.view insertSubview:glass belowSubview:textField];
}
-(void)hideGlass{
//remove your glass.
[glass removeFromSuperview];
//your textField resigns first responder.
if([myTextField canResignFirstResponder]){
[myTextField resignFirstResponder];
}
}
So basically what you do, is to add a dummy button right bellow your textField. So when you touch anything else, excepts your textField, he will make your textField resignFirstResponder and removes himself from the view.
Edit 1 ( the tweek) You just need to replace this:
if(glass){
self.glass=nil;
}
for this:
if(glass){
[glass release];
glass=nil;
}
You just need to implement the following delegate methods:
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[textField resignFirstResponder];
return YES;
}
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField {
[textField resignFirstResponder];
return YES;
}
If you have an IBOutlet for the UITextField, you can dismiss the keyboard using [textField resignFirstResponder];. This being said, you will have to implement event listeners for everything else on the view. If you want the keyboard to hide when the user taps the view's background, it can be done through a touch event in the view controller's implementation:
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[textField resignFirstResponder];
}
If you have any buttons or subviews, you will have to implement individual touch events or actions for those as well.