217

Note:

See accepted answer (not top voted one) for solution as of iOS 4.3.

This question is about a behavior discovered in the iPad keyboard, where it refuses to be dismissed if shown in a modal dialog with a navigation controller.

Basically, if I present the navigation controller with the following line as below:

navigationController.modalPresentationStyle = UIModalPresentationFormSheet;

The keyboard refuses to be dismissed. If I comment out this line, the keyboard goes away fine.

...

I've got two textFields, username and password; username has a Next button and password has a Done button. The keyboard won't go away if I present this in a modal navigation controller.

WORKS

broken *b = [[broken alloc] initWithNibName:@"broken" bundle:nil];
[self.view addSubview:b.view];

DOES NOT WORK

broken *b = [[broken alloc] initWithNibName:@"broken" bundle:nil];
UINavigationController *navigationController = 
[[UINavigationController alloc]
 initWithRootViewController:b];
navigationController.modalPresentationStyle = UIModalPresentationFormSheet;
navigationController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:navigationController animated:YES];
[navigationController release];
[b release];

If I remove the navigation controller part and present 'b' as a modal view controller by itself, it works. Is the navigation controller the problem?

WORKS

broken *b = [[broken alloc] initWithNibName:@"broken" bundle:nil];
b.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:b animated:YES];
[b release];

WORKS

broken *b = [[broken alloc] initWithNibName:@"broken" bundle:nil];
UINavigationController *navigationController = 
    [[UINavigationController alloc]
         initWithRootViewController:b];
[self presentModalViewController:navigationController animated:YES];
[navigationController release];
[b release];
Irfan
  • 4,301
  • 6
  • 29
  • 46
Kalle
  • 13,186
  • 7
  • 61
  • 76
  • The following SO question seems to be having the same problem, but there are no answers: http://stackoverflow.com/questions/3019709/modal-dialog-does-not-dismiss-keyboard – Kalle Aug 02 '10 at 09:51
  • +1 Thank you for your great explanation. But where do I have to put that method? It seems not working where I create the code for presenting the model controller... – Lorenzo B Jun 26 '12 at 16:24
  • 1
    It has to be in the modal view controller class itself. – Kalle Jun 26 '12 at 17:01
  • Thanks. I see. I solved putting it in a category for `UINavigationController` class. Cheers. – Lorenzo B Jun 27 '12 at 07:23
  • I'm so indebted to you for this question. I was surprised that `resignFirstResponder` was executing but the keyboard still being shown. My scenario (presentationFormSheet with navig contrllr) is exactly the same as yours. Thanks a ton!! – sErVerdevIL Jan 09 '13 at 11:23

13 Answers13

173

This has been classified as "works as intended" by Apple engineers. I filed a bug for this a while back. Their reasoning is that the user is often going to be entering data in a modal form so they are trying to be "helpful" and keep the keyboard visible where ordinarily various transitions within the modal view can cause the keyboard to show/hide repeatedly.

edit: here is the response of an Apple engineer on the developer forums:

Was your view by any chance presented with the UIModalPresentationFormSheet style? To avoid frequent in-and-out animations, the keyboard will sometimes remain on-screen even when there is no first responder. This is not a bug.

This is giving a lot of people problems (myself included) but at the moment there doesn't seem to be a way to work around it.

UPDATE:

In iOS 4.3 and later, you can now implement `-disablesAutomaticKeyboardDismissal' on your view controller to return NO:

- (BOOL)disablesAutomaticKeyboardDismissal {
    return NO;
}

This fixes the issue.

Mike Weller
  • 45,401
  • 15
  • 131
  • 151
  • 7
    *pause* Wow, okay. Thanks a lot for the heads up. Damn Apple.. :( – Kalle Aug 02 '10 at 10:29
  • Did you submit a bug report to Apple? I have done so under ID# 8384423. I have also submitted a sample application to reproduce the behaviour. – Shaggy Frog Sep 02 '10 at 02:08
  • MishieMoo: nice! Glad Apple are acknowledging that, at least. – Kalle Sep 16 '10 at 16:01
  • Wow, that is retarded. You'd think that they'd create a flexible solution that would enable us to meet our client's use cases...but instead they create the use case for the client they don't even know! :S – PostCodeism Nov 26 '10 at 03:06
  • 3
    As of iOS 4.3 there is now a disablesAutomaticKeyboardDismissal method which fixes this problem. – Kalle Mar 31 '11 at 19:06
  • 5
    i try that disablesAutomaticKeyboardDismissal method, but it still did not solve the problem, how to solve it? – R. Dewi Sep 21 '11 at 02:06
  • Unfortunately, this solution is broken again in iOS 6 :-( – Snips Oct 26 '12 at 23:25
  • 3
    @Snips: You need to create a `UINavigationController` subclass that overrides `disablesAutomaticKeyboardDismissal` to return `NO` and use this as the navigation controller when you present a modal form sheet. See the answer from @miha-hribar below. – Pascal Oct 27 '12 at 00:20
151

Be careful if you are displaying the modal with a UINavigationController. You then have to set the disablesAutomaticKeyboardDismissal on the navigation controller and not on the view controller. You can easily do this with categories.

File: UINavigationController+KeyboardDismiss.h

#import <Foundation/Foundation.h>

@interface UINavigationController (KeyboardDismiss)

- (BOOL)disablesAutomaticKeyboardDismissal;

@end

File: UINavigationController+KeyboardDismiss.m

#import "UINavigationController+KeyboardDismiss.h"

@implementation UINavigationController(KeyboardDismiss)

- (BOOL)disablesAutomaticKeyboardDismissal
{
    return NO;
}

@end

Do not forget to import the category in the file where you use the UINavigationController.

MrHus
  • 32,888
  • 6
  • 31
  • 31
Miha Hribar
  • 5,776
  • 3
  • 26
  • 24
  • 20
    +1, finally I see the missing piece of information for this issue highlighted: that one needs to override `disablesAutomaticKeyboardDismissal` of `UINavigationController`, not the own view controller, to fix this issue. – DarkDust Oct 26 '11 at 12:55
  • Nice! Just what I needed. Thank you. – Justin Dec 02 '11 at 11:50
  • Perfect. Not clear from official docs but makes sense due to the UINavigationController being in the responder chain. Excellent answer. Thank you! – imnk Apr 17 '12 at 09:26
  • This bit of knowledge fixed my problem immediately. Thanks! – Mark May 18 '12 at 21:20
  • 1
    I'm presenting a modal dialog from a UISplitViewController. I've tried the above code, but substituted UISplitViewController for UINavigationController, but it still doesn't work. Should this method also work on a UISplitViewController? – Snips Oct 27 '12 at 14:42
  • Thanks for providing a solution that actually works with an explanation that's succinct and easy to follow. – Jono Jan 19 '13 at 23:08
  • i created this category and imported it in the view controller that i'll be using. now what? do i need to override anything or add any code to dismiss the keyboard in the viewController? – HusseinB Apr 09 '13 at 20:24
  • 7
    It's not a good idea to implement a duplicate method in a category. You can never be certain which implementation will be called, so at best you can expect inconsistent behavior. Better to inherit from UINavigationController and override the method in your custom class. – sean woodward Apr 18 '13 at 20:32
  • Man, how didn't I think about this solution?! Usually, as sean said, it's better to use subclass in these cases... – Natan R. Jul 18 '13 at 08:40
118

In the view controller that is presented modally, just override disablesAutomaticKeyboardDismissal to return NO:

- (BOOL)disablesAutomaticKeyboardDismissal {
    return NO;
}
Greg Brown
  • 3,168
  • 1
  • 27
  • 37
Sebastian H
  • 1,196
  • 1
  • 8
  • 2
52

I solved this by using the UIModalPresentationPageSheet presentation style and resizing it immediately after I present it. Like so:

viewController.modalPresentationStyle = UIModalPresentationPageSheet;
viewController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:viewController animated:YES];
viewController.view.superview.autoresizingMask = 
    UIViewAutoresizingFlexibleTopMargin | 
    UIViewAutoresizingFlexibleBottomMargin;    
viewController.view.superview.frame = CGRectMake(
    viewController.view.superview.frame.origin.x,
    viewController.view.superview.frame.origin.y,
    540.0f,
    529.0f
);
viewController.view.superview.center = self.view.center;
[viewController release];
Dharmesh Dhorajiya
  • 3,976
  • 9
  • 30
  • 39
dvs
  • 12,324
  • 6
  • 38
  • 45
  • Hmmm...this is not quite right...resizing causes the modal to paint funny...it's like it squishes the content down to fit in the new size box or something...everything looks funny. :( – toofah Jan 07 '11 at 16:53
  • There are also rotation problems with this one...if you rotate while this modal is up it will shrink/grow as if it were a full page view – toofah Jan 07 '11 at 17:07
  • 3
    toofah, I edited the code to deal with the shrinking/growing problem when rotating; just a matter of giving the superview a flexible top and bottom margin. I'm not sure I'm seeing the other behavior. – dvs Jan 08 '11 at 19:53
  • 1
    this works only as long as you don't push a other view on top of this one. Because when you close the view pushed above the UIModalPresentationPageSheet presented view, it's back to it's original size. – V1ru8 Apr 07 '11 at 15:38
  • It worked. But the word in the view looks a little blur. I don't know why. – jeswang Aug 23 '12 at 08:00
  • I usually use the same solution for resizing a modalview. But I realize that it doesn't work for iOS 6. The view always gets stuck at the top of parent view. Any solution?? thanks – Frade Dec 03 '12 at 17:19
1

Put this code in your viewWillDisappear: method of current controller is another way to fix this:

Class UIKeyboardImpl = NSClassFromString(@"UIKeyboardImpl");
id activeInstance = [UIKeyboardImpl performSelector:@selector(activeInstance)];
[activeInstance performSelector:@selector(dismissKeyboard)];
Story
  • 161
  • 1
  • 4
1

I found that disablesAutomaticKeyboardDismissal and adding a disablesAutomaticKeyboardDismissal function didn't work for my UITextField in a modal dialog.

The onscreen keyboard just wouldn't go away.

My solution was to disable all text-input controls in my dialog, then re-enable the relevant ones a fraction of a second later.

It seems as though when iOS sees that none of the UITextField controls are enabled, then it does get rid of the keyboard.

Mike Gledhill
  • 27,846
  • 7
  • 149
  • 159
1

If you toggle a different modal display you can get the keyboard to disappear. It's not pretty and it doesn't animate down, but you can get it to go away.

It'd be great if there was a fix, but for now this works. You can wedge it in a category on UIViewController and call it when you want the keyboard gone:

@interface _TempUIVC : UIViewController
@end

@implementation _TempUIVC
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    return YES;
}
@end

@implementation UIViewController (Helpers)

- (void)_dismissModalViewController {
    [self dismissModalViewControllerAnimated:NO];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidHideNotification object:nil];
    [self release];
}

- (void)forceKeyboardDismissUsingModalToggle:(BOOL)animated {
    [self retain];
    _TempUIVC *tuivc = [[_TempUIVC alloc] init];
    tuivc.modalPresentationStyle = UIModalPresentationCurrentContext;
    [self presentModalViewController:tuivc animated:animated];
    if (animated) {
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_dismissModalViewController) name:UIKeyboardDidHideNotification object:nil];
    } else
        [self _dismissModalViewController];
    [tuivc release];
}

@end

Be careful with this though as you viewDidAppear / viewDidDisappear and all those methods get called. Like I said, it's not pretty, but does work.

-Adam

Adam
  • 1,467
  • 1
  • 15
  • 15
1

You could also work around this in a universal app by simply checking the idiom and if it's an iPad, don't pop up the keyboard automatically at all and let the user tap whatever they want to edit.

May not be the nicest solution but it's very straightforward and doesn't need any fancy hacks that will break with the next major iOS release :)

Maciej Swic
  • 11,139
  • 8
  • 52
  • 68
0

For those having trouble with UINavigationController, see my answer to a similar question here: https://stackoverflow.com/a/10507689/321785

Edit: I consider this an improvement to Miha Hribar's solution (since the decision is taking place where it should), and as opposed to Pascal's comment regarding a category on UIViewController

Community
  • 1
  • 1
Chris Trahey
  • 18,202
  • 1
  • 42
  • 55
0

may be not a perfect solution ,but works
[self.view endEditing:YES];
from wherever your button or gesture is implemented to present modal

Tanuj Jagoori
  • 309
  • 3
  • 9
0

maybe don't return NO, but YES. So it can go away.

And you have a textFieldShouldEndEditing returning YES as well?

And why are you firing [nextResponder becomeFirstResponder]?! sorry i see now

I also have a number of UITextViews which all have their "editable" property set to FALSE.

May we assume none of them, by any chance, has a tag value of secondField.tag+1? If so, you're telling them to become first responder, instead of resigning the first responder. Maybe put some NSLog() in that if structure.

mvds
  • 45,755
  • 8
  • 102
  • 111
  • 1
    NO = don't insert newline, from what I can tell. And setting it to YES didn't fix it. – Kalle Jul 30 '10 at 14:36
  • 1
    A UITextField, being one line by definition, doesn't do much with newlines, I think. So it's more about processing pressing of the Return/Done button, as stated in the docs. – mvds Jul 30 '10 at 14:51
  • Are you very sure you hooked everything up the right way? Have you put an `NSLog("tf %x / method ...",textField);` in all delegate functions? – mvds Jul 30 '10 at 15:04
  • Well, the delegate functions are called appropriately, and they wouldn't be if the delegate wasn't set up appropriately. And the NSLog gives an EXC_BAD_ACCESS. Also warns me about it being incompatible type in XCode. – Kalle Aug 01 '10 at 08:34
  • D'oh. Sorry, should've seen that myself. I've updated the answer above with the results of these NSLogs since formatting will gook itself in comm.. – Kalle Aug 01 '10 at 10:27
  • put some NSLog's within the if as well to see what's going on exactly. see my updated answer btw. – mvds Aug 01 '10 at 10:42
  • Can't you just zip the entire thing and post it somewhere? I want to believe "all is right but it doesn't work" but I'd like to see it happening with my own eyes... – mvds Aug 01 '10 at 13:43
  • Can't, because it's a work project and all that, but what I'll do is (and I should do this, anyway) make a new project and merge this part of it over. Presumably it will WORK, but in the off chance that it doesn't, I can post that project. Give me a day or so to do it though. Will update answer and such when it's done. – Kalle Aug 01 '10 at 14:44
  • Huh! While working on getting this into its own project by itself, I realized the following: the keyboard DOES GO AWAY if the view IS NOT A MODAL VIEW. If the view is, the keyboard stays; if the view is not, the keyboard goes away. Still no idea how to solve it but will post the two versions above in my answer in a sec... – Kalle Aug 02 '10 at 09:32
  • I've updated the answer above and cleaned it up a bit. In summary, the following line is causing the problem: navigationController.modalPresentationStyle = UIModalPresentationFormSheet; -- if I remove it, the keyboard will dismiss fine. I can only presume this is a bug. – Kalle Aug 02 '10 at 10:07
0

I'm sure you have looked at this, but you are sure that your controller class is properly hooked up as the UITextField delegate, right?

Neal L
  • 4,329
  • 8
  • 34
  • 39
0
Swift 4.1:
extension UINavigationController {
   override open var disablesAutomaticKeyboardDismissal: Bool {
      return false
   }
}
fivewood
  • 391
  • 4
  • 11