iPad键盘无法关闭,如果模态视图控制器的呈现样式为UIModalPresentationFormSheet。

217

注意:

查看已被接受的答案(不是最高票答案),适用于iOS 4.3。

问题涉及iPad键盘中发现的一种行为,即如果在带有导航控制器的模态对话框中显示,它将拒绝被解除。

基本上,如果我使用以下代码行呈现导航控制器,则会出现该问题:

navigationController.modalPresentationStyle = UIModalPresentationFormSheet;

键盘无法关闭。如果我注释掉这一行,键盘就能正常关闭。

...

我有两个文本框,用户名和密码;用户名有一个“下一个”按钮,密码有一个“完成”按钮。如果我在模态导航控制器中呈现它,键盘将无法关闭。

有效

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

无效

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];
如果我删除导航控制器部分,并将 'b' 自身作为模态视图控制器呈现,那么它就能正常工作。是导航控制器造成的问题吗? 正常工作
broken *b = [[broken alloc] initWithNibName:@"broken" bundle:nil];
b.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:b animated:YES];
[b release];

工作正常

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

以下的SO问题似乎有相同的问题,但没有答案:https://dev59.com/iXA75IYBdhLWcg3w899J - Kalle
+1 谢谢您的详细解释。但是我应该把这个方法放在哪里呢?在我创建用于呈现模型控制器的代码中似乎无法正常工作... - Lorenzo B
1
它必须在模态视图控制器类本身中。 - Kalle
谢谢。我明白了。我把它放在了UINavigationController类别中解决了。干杯。 - Lorenzo B
非常感谢您提出这个问题。我很惊讶resignFirstResponder被执行,但键盘仍然显示。我的情况(presentationFormSheet与navig contrllr)与您的完全相同。非常感谢! - sErVerdevIL
13个回答

173
这被苹果的工程师们归类为“按预期工作”。我之前报告了一个错误。他们的理由是用户通常会在模态表单中输入数据,所以他们试图“有所帮助”,保持键盘可见,否则模态视图中的各种转换可能会导致键盘反复显示/隐藏。
编辑:这里是开发者论坛上苹果工程师的回复:
“你的视图是否使用了UIModalPresentationFormSheet样式?为了避免频繁的进出动画,即使没有第一响应者,键盘有时也会保留在屏幕上。这不是一个错误。”
这给很多人带来了问题(包括我自己),但目前似乎没有解决方法。
更新:
在iOS 4.3及更高版本中,您现在可以在您的视图控制器上实现`-disablesAutomaticKeyboardDismissal'`并返回NO:
- (BOOL)disablesAutomaticKeyboardDismissal {
    return NO;
}

这解决了问题。


7
好的,谢谢提醒。该死的苹果... :( - Kalle
3
从iOS 4.3开始,现在有一个disablesAutomaticKeyboardDismissal方法来解决这个问题。 - Kalle
5
我尝试使用disablesAutomaticKeyboardDismissal方法,但仍未解决问题,该怎么办? - R. Dewi
很遗憾,这个解决方案在iOS 6中又出现了问题 :-( - Snips
3
您需要创建一个UINavigationController子类,覆盖disablesAutomaticKeyboardDismissal方法并返回NO,然后将其用作显示模态表单视图时的导航控制器。请参考下面@miha-hribar的回答。 - Pascal
显示剩余3条评论

151

如果您使用 UINavigationController 显示模态视图,请注意。您需要在导航控制器上设置 disablesAutomaticKeyboardDismissal,而不是在视图控制器上设置。您可以通过分类轻松完成此操作。

文件:UINavigationController+KeyboardDismiss.h

#import <Foundation/Foundation.h>

@interface UINavigationController (KeyboardDismiss)

- (BOOL)disablesAutomaticKeyboardDismissal;

@end

文件: UINavigationController+KeyboardDismiss.m

#import "UINavigationController+KeyboardDismiss.h"

@implementation UINavigationController(KeyboardDismiss)

- (BOOL)disablesAutomaticKeyboardDismissal
{
    return NO;
}

@end

不要忘记在使用UINavigationController的文件中导入该类别。


20
+1,终于看到了这个问题缺失的关键信息:需要覆盖UINavigationControllerdisablesAutomaticKeyboardDismissal而不是自己的视图控制器,以解决此问题。 - DarkDust
1
我从UISplitViewController中呈现了一个模态对话框。我尝试使用上面的代码,但将UINavigationController替换为UISplitViewController,但仍然无法正常工作。这种方法在UISplitViewController上是否也适用? - Snips
感谢您提供一个实际可行的解决方案,并且解释简洁易懂,易于跟随。 - Jono
7
在类别中实现重复方法并不是一个好主意。你无法确定哪个实现会被调用,因此最好预料到行为不一致。更好的方式是继承 UINavigationController 并在自定义类中覆盖该方法。 - sean woodward
哇,我怎么没想到这个解决方案呢?通常像 Sean 说的那样,在这些情况下最好使用子类... - Natan R.
显示剩余4条评论

118
在被模态呈现的视图控制器中,只需覆盖 disablesAutomaticKeyboardDismissal 方法并返回 NO
- (BOOL)disablesAutomaticKeyboardDismissal {
    return NO;
}

是的,从4.3版本开始似乎是这样。我会更新问题的。谢谢! - Kalle
2
这需要添加到导航控制器中。 - pottedmeat
1
是的,在NavigationController中覆盖它时可以工作。这是我实际上唯一有效的方法。 - James Laurenstin
救命稻草!为什么苹果会做这样的事情?它肯定应该默认为“否”,并允许我们在确实想要更改时进行更改。 - SomaMan
在UIViewController派生类上无法工作,disablesAutomaticKeyboardDismissal从未被调用。 - Jorge Arimany

52

我通过使用UIModalPresentationPageSheet呈现样式并在呈现后立即调整大小来解决了这个问题。像这样:

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];

嗯...这不太对...重新调整大小会导致模态框呈现出奇怪的画面...就像它把内容压缩到新的大小框中或者其他什么...一切看起来都很奇怪。:( - toofah
这个也存在旋转问题...如果你在弹出模态框时旋转,它会像全屏视图一样缩小/放大。 - toofah
3
Toofah,我修改了代码以解决旋转时收缩/扩展的问题;只需要给父视图设置灵活的顶部和底部边距。我不确定我是否看到了其他行为。 - dvs
1
只要您不在此视图之上推入其他视图,它就能正常工作。因为当您关闭位于UIModalPresentationPageSheet上方的视图时,它会恢复到原始大小。 - V1ru8
它可以工作。但是视图中的单词看起来有点模糊。我不知道为什么。 - jeswang
我通常使用相同的解决方案来调整模态视图的大小。但是我意识到它在iOS 6上不起作用。视图总是卡在父视图的顶部。有什么解决办法吗?谢谢。 - Frade

1

如果您切换不同的模态显示,您可以让键盘消失。这并不美观,也没有动画效果,但您可以让它消失。

如果有解决方法那就太好了,但现在这个方法可行。您可以将其嵌入到UIViewController类别中,并在需要让键盘消失时调用它:

@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

注意:当你调用viewDidAppear / viewDidDisappear等方法时要小心。像我说的那样,这并不美观,但确实有效。

- Adam


1
将以下代码放在当前控制器的viewWillDisappear:方法中是另一种解决方法:
Class UIKeyboardImpl = NSClassFromString(@"UIKeyboardImpl");
id activeInstance = [UIKeyboardImpl performSelector:@selector(activeInstance)];
[activeInstance performSelector:@selector(dismissKeyboard)];

1
我发现在模态对话框中,disablesAutomaticKeyboardDismissal和添加disablesAutomaticKeyboardDismissal函数都不能让我的UITextField正常工作,屏幕键盘就是无法消失。
我的解决方案是先禁用对话框中所有文本输入控件,然后在稍后的一瞬间重新启用相关控件。看起来,当iOS发现没有任何一个UITextField控件被启用时,它才会关闭键盘。

1
你也可以通过检查 idiom 并确定它是否为 iPad,来解决这个问题,不自动弹出键盘,并让用户点击他们想要编辑的内容。这可能不是最好的解决方法,但非常直接,不需要任何花哨的技巧,在下一个 iOS 主要版本发布后也不会失效 :)

0
Swift 4.1:
extension UINavigationController {
   override open var disablesAutomaticKeyboardDismissal: Bool {
      return false
   }
}

0

对于那些在使用UINavigationController时遇到问题的人,请查看我在这里类似问题的答案: https://dev59.com/iXA75IYBdhLWcg3w899J#10507689

编辑: 我认为这是Miha Hribar解决方案的改进(因为决策正在发生应该发生的地方),与Pascal关于UIViewController类别的评论相反。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接