views:

4858

answers:

8

I have quite a few controls scattered throughout many table cells in my table, and I was wondering if there's an easier way to dismiss the keyboard without having to loop through all my controls and resigning them all as the first responder. I guess the question is.. How would I get the current first responder to the keyboard?

+10  A: 

Here's one idea:

@interface ... {
    UITextField *editingField;
}

- (void)textFieldDidBeginEditing:(UITextField *)textField;
{
    editingField = textField;
}

- (IBAction)dismissKeyboard:(id)sender;
{
    [editingField resignFirstResponder];
}

Then make that class the delegate of all your UITextFields. You could also subclass UITextField (or whatever other control you're using) and override -becomeFirstResponder.

Edit: The other suggested method is better. Do something like this:

@interface ... {
    UITextField *someField;
}

- (IBAction)dismissKeyboard:(id)sender;
{
    [someField becomeFirstResponder];
    [someField resignFirstResponder];
}

and bind someField to any UITextField in IB. (Note that this doesn't work on desktop Cocoa, where becomeFirstResponder is only a notification method; you need to use NSWindow methods. Then again, desktop Cocoa actually has a method to ask for the first responder.)

Nicholas Riley
good answer +1 ;-)
Raj
But that will only block the component from becoming firstResponder, not remove current firstResponder status from an element.
Kendall Helmstetter Gelner
What I meant in overriding becomeFirstResponder to store the first responder somewhere, so it can be accessed (in this case, resigned) later. The basic problem is that Cocoa Touch does not provide a way to ask a window or view what its first responder is.
Nicholas Riley
+5  A: 

A better approach is to have something "steal" first responder status.

Since UIApplication is a subclass of UIResponder, you could try:

[[UIApplication sharedApplication] becomeFirstResponder]
[[UIApplication sharedApplication] resignFirstResponder]

Failing that, create a new UITextField with a zero sized frame, add it to a view somewhere and do something similar (become followed by resign).

Kendall Helmstetter Gelner
The UIApplication trick doesn't work (it crashes, at least on the simulator) but you don't need a zero-sized UITextField - just pick any random field and do this with it. NSResponder's becomeFirstResponder is a notification only method, yet UIResponder's isn't (the design is worse, actually).
Nicholas Riley
Aha, thanks! It crashes calling it on UIApplication but the zero size UITextField works great, and I don't have to retrieve any of my previous fields.
Seventoes
+2  A: 

i am not really sure why one would need to go through all this.

consider this scenario:

i have a viewcontroller with two textfields (username and password). and the viewcontroller implements UITextFieldDelegate protocol

i do this in viewDidLoad

- (void)viewDidLoad 
{
    [super viewDidLoad];

    username.delegate = self;
    password.delegate = self;
}

and the viewcontroller implements the optional method as

- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
    [textField resignFirstResponder];
    return YES;
}

and irrespective of the textfield you are in, as soon as i hit return in the keyboard, it gets dismissed!

In your case, the same would work as long as you set all the textfield's delegate to self and implement textFieldShouldReturn

Please correct me if i am missing the obvious!!

Cheers - Prakash

Prakash
Well I already have the problem solved, but in my case I wanted to dismiss the keyboard on my own (in code), and I didn't know which textField had first responder status.
Seventoes
A: 

I hate that there's no "global" way to programmatically dismiss the keyboard without using private API calls. Frequently, I have the need to dismiss the keyboard programmatically without knowing what object is the first responder. I've resorted to inspecting the self using the Objective-C runtime API, enumerating through all of its properties, pulling out those which are of type UITextField, and sending them the resignFirstResponder message.

It shouldn't be this hard to do this...

LucasTizma
A: 

It's not pretty, but the way I resign the firstResponder when I don't know what that the responder is:

Create an UITextField, either in IB or programmatically. Make it Hidden. Link it up to your code if you made it in IB. Then, when you want to dismiss the keyboard, you switch the responder to the invisible text field, and immediately resign it:

  [self.invisibleField becomeFirstResponder];
  [self.invisibleField resignFirstResponder];
cannyboy
A: 

You can recursively iterate through subviews, store an array of all UITextFields, and then loop through them and resign them all.

Not really a great solution, especially if you have a lot of subviews, but for simple apps it should do the trick.

I solved this in a much more complicated, but much more performant way, but using a singleton/manager for the animation engine of my app, and any time a text field became the responder, I would assign assign it to a static which would get swept up (resigned) based on certain other events... its almost impossible for me to explain in a paragraph.

Be creative, it only took me 10 minutes to think through this for my app after I found this question.

Jasconius
Solved this months ago ;) I ended up just saving the active field on focus and using that.
Seventoes
+1  A: 

[self.view endEditing:TRUE];

kirby
This does seem to work (tested on ios4.1). Though it is only available on iOS 3.2 upwards. I'm not sure if this is actually guaranteed to work, or is just a non-guaranteed side effect from calling an API in a way that it's not documented to work.
JosephH
A: 

@Nicholas Riley & @Kendall Helmstetter Geln & @cannyboy:

Absolutely brilliant!

Thank you.

Considering your advice and the advice of others in this thread, this is what I've done:

What it looks like when used:

[[self appDelegate] dismissKeyboard]; (note: I added appDelegate as an addition to NSObject so I can use anywhere on anything)

What it looks like under the hood:

- (void)dismissKeyboard {

UITextField *tempTextField = [[UITextField alloc] initWithFrame:CGRectZero];
[_myRootViewController.view addSubView:tempTextField];
[tempTextField becomeFirstResponder];
[tempTextField resignFirstResponder];
[tempTextField removeFromSuperview];
[tempTextField release];

}
Jeremy