轻松解决键盘弹出的方法?

383

我在表格的许多单元格中散落着相当多的控件,我想知道是否有一种更容易的方法可以不必循环遍历所有我的控件并将它们全部取消作为第一个响应者来关闭键盘。我猜问题是...我该如何获得当前第一响应者到键盘?


这是一个很好的补充问题:https://dev59.com/hXI_5IYBdhLWcg3wMf5_ - ericsoco
以下是对所有答案的警告。如果您在取消键盘后立即执行segue,则键盘将变为孤儿,无法被解除。您必须在textFieldShouldReturn方法中解除键盘。 - gjpc
29
全局键盘收起的正确方式:[[[UIApplication sharedApplication] keyWindow] endEditing:YES]; - Yerk
我发现这个视频很有帮助 https://pinkstone.co.uk/how-to-dismiss-the-keyboard-from-a-uitextfield-in-ios/ 。也许它能帮到其他人。 - enthusiasticgeek
31个回答

807

尝试:

[self.view endEditing:YES];

3
这对我来说非常有效,肯定比这个问题的其他答案更可取。文档上说它在iOS 2.0及更高版本中可用。 - antsyawn
8
根据文档所述:“此方法查找当前视图及其子视图层次结构中当前为第一响应者的文本字段。如果找到一个,则要求该文本字段放弃作为第一响应者的身份。如果将force参数设置为YES,则甚至不会询问文本字段,而是强制其放弃身份。”- 因此,在我看来,这是原始问题的正确答案(使用情况:我有一个带有数百万个字段 - 好吧,大约十个...左右,并且我需要关闭键盘)。 - joshis
14
答案并没有错,但有一点不正确,实际上应该是: [self.view endEditing:YES]; 因为在Objective-C中,BOOL类型的取值只有YES/NO。 - chrisben
24
可以使用YES/NO、TRUE/FALSE、0/1。 - Michael Superczynski
1
如果你正在进行窗口操作,比如从模态单例中,你可能只想在窗口上执行它:[[UIApplication sharedApplication].keyWindow endEditing:YES]; - Shawn
显示剩余4条评论

126

你可以通过 [view endEditing:YES] 强制当前正在编辑的视图放弃第一响应者状态,从而隐藏键盘。

-[UIResponder resignFirstResponder] 不同,-[UIView endEditing:] 将搜索子视图以查找当前的第一响应者。因此,您可以将其发送到顶级视图(例如在 UIViewController 中的 self.view),它将执行正确的操作。

(此答案先前包括其他几个解决方案,这些解决方案也起作用,但比必要的复杂。为避免混淆,我已将它们删除。)


但这只会阻止组件成为firstResponder,而不能从元素中删除当前的firstResponder状态。 - Kendall Helmstetter Gelner
3
这已经过时了。使用[self.view endEditing:YES];会更好。 - ftvs
同意 - 你能看出我实际上不是iOS开发者吗?我已经修复了它,因为这个答案似乎被使用了。 - Nicholas Riley
我在使用[self.view endEditing:YES];时遇到了问题。我在UITableView中触发它,当用户开始滚动时。当这种情况发生时,滚动条很小,我可以在表视图下面滚动。松开手指再次滚动会解决这个问题,但这相当烦人... - Matej
1
这并不过时,这个答案详细解释了[UIView endediting:]的工作原理。在我开发的一个应用程序中,一个搜索框被添加到了UINavigationViewController中,如果你只是调用[self.view endEditing:YES],作为你的视图控制器的self,这将不起作用,相反,我直接在搜索框添加的视图范围内调用了这个方法,例如:[self.mySearchBoxView endEditing:YES] - Jan Cássio
显示剩余4条评论

95
您可以向应用程序发送一个空目标动作,它会在任何时候放弃第一响应者,而无需担心哪个视图当前拥有第一响应者状态。
Objective-C:
[[UIApplication sharedApplication] sendAction:@selector(resignFirstResponder) to:nil from:nil forEvent:nil];

Swift 3.0:

UIApplication.shared.sendAction(#selector(resignFirstResponder), to: nil, from: nil, for: nil)

在 Mac OS X 上,零目标操作对于菜单命令是很常见的,而在 iOS 上,这里有一个用途。


14
整个页面的最佳解决方案。 - Léo Natan
在iOS应用程序中摆脱键盘的最佳方法。 - pronebird
在iOS8中,这对我仍然有效。在我的应用程序中,我有一个侧边栏菜单,有时“中心”视图控制器会显示键盘。如果侧边栏菜单将显示,我想关闭键盘。因此,我在我的侧边栏菜单类的viewWillAppear方法中添加了这段代码。 - JCurativo
1
仍然适用于iOS 9。是一个很好的全局重置文本字段焦点和关闭键盘的方法。 - bmjohns
继续这个特定的评论串....仍然适用于iOS 10. :-) - Travelling Man

56

说实话,我对这里提出的任何解决方案都不是很满意。但我找到了一种很好的使用TapGestureRecognizer的方法,我认为它能够解决您的问题:当您点击除键盘以外的任何东西时,关闭键盘。

  1. 在viewDidLoad中,注册接收键盘通知并创建一个UITapGestureRecognizer:

NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];

[nc addObserver:self selector:@selector(keyboardWillShow:) name:
UIKeyboardWillShowNotification object:nil];

[nc addObserver:self selector:@selector(keyboardWillHide:) name:
UIKeyboardWillHideNotification object:nil];

tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
action:@selector(didTapAnywhere:)];
  • 添加显示/隐藏键盘的响应者。 在此处,您可以向应该在单击时解除键盘的UIView添加和删除TapGestureRecognizer。 注意:您不必将其添加到所有子视图或控件中。

  • -(void) keyboardWillShow:(NSNotification *) note {
        [self.view addGestureRecognizer:tapRecognizer];
    }
    
    -(void) keyboardWillHide:(NSNotification *) note
    {
        [self.view removeGestureRecognizer:tapRecognizer];
    }
    
  • 当用户点击屏幕时,TapGestureRecognizer将调用您的函数,您可以通过以下方式取消键盘:

  • -(void)didTapAnywhere: (UITapGestureRecognizer*) recognizer {    
        [textField resignFirstResponder];
    }
    
    这个解决方案的好处在于它只过滤了“Tap”而不是“Swipe”。因此,如果您在键盘上方有滚动内容,则“swipes”仍然会滚动并保留键盘显示。在键盘消失后删除手势识别器后,视图上未来的轻触将被正常处理。

    我在我的 @implementation 中添加了以下代码: @property (assign) UITapGestureRecognizer *tapRecognizer; 并修改了 self.tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTapAnywhere:)]; - Professor Todd
    我正在尝试做类似的事情,但是在点击时没有得到回调。好奇:为什么你的属性是“assign”? - TahoeWolverine
    加一,而且这应该是默认的iOS行为。或者至少添加一个简单的方式来切换关闭按钮。 - Shaunti Fondrisi

    24

    这是一个解决方案,使键盘在按下任何文本字段中的return时消失。只需在一个位置添加代码(因此不必为每个文本字段添加处理程序):


    考虑以下场景:

    我有一个带有两个文本字段(用户名和密码)的viewcontroller。 并且viewcontroller实现了UITextFieldDelegate协议。

    我在viewDidLoad中进行如下操作:

    - (void)viewDidLoad 
    {
        [super viewDidLoad];
    
        username.delegate = self;
        password.delegate = self;
    }
    

    并且视图控制器实现了可选方法,代码如下:

    - (BOOL)textFieldShouldReturn:(UITextField *)textField
    {
        [textField resignFirstResponder];
        return YES;
    }
    
    无论你在哪个文本字段中,一旦我按下键盘上的 return ,它就会被关闭!在你的情况下,只要将所有文本字段的委托设置为 self 并实现 textFieldShouldReturn ,同样的方法也会起作用。

    我已经解决了这个问题,但在我的情况下,我想自己(在代码中)关闭键盘,而我不知道哪个textField具有第一响应者状态。 - Seventoes
    1
    有时候,您可能希望在不按“返回”键的情况下关闭键盘,例如当侧边栏菜单即将显示时。 - Peter Johnson
    编辑以澄清此答案仅适用于按“返回”键是可接受解决方案的情况。在我的情况下,我还希望在用户点击正在编辑的字段之外的区域时关闭键盘;此答案无法解决这个问题。 - ToolmakerSteve

    14

    更好的方法是让某个东西“偷走”第一个响应者的状态。

    由于UIApplication是UIResponder的子类,因此您可以尝试以下操作:

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

    如果不行的话,创建一个大小为零的新UITextField,将其添加到某个视图中,然后执行类似的操作(先调用becomeFirstResponder再调用resignFirstResponder)。


    1
    UIApplication的技巧不起作用(至少在模拟器上会崩溃),但您不需要一个零大小的UITextField - 只需选择任意随机字段并对其执行此操作即可。NSResponder的becomeFirstResponder仅是通知方法,而UIResponder的不是(实际上设计更糟糕)。 - Nicholas Riley
    1
    啊哈,谢谢!在UIApplication上调用它会崩溃,但是零大小的UITextField很好用,我不需要检索任何以前的字段。 - Seventoes
    如果有官方的、完美的、有文档支持的方法,为什么还会有人这样做呢? - Maciej Swic
    2
    他们不会这样做的,可以看一下最多赞同的答案,那才是更好的解决方案。只是当时很少人知道(我的答案是在2009年,另一个答案则在2010年晚了一年多)。但我将其留给后世。 - Kendall Helmstetter Gelner

    8

    将此代码放入某个实用类中。

    + (void)dismissKeyboard {
        [self globalResignFirstResponder];
    }
    
    + (void) globalResignFirstResponder {
        UIWindow * window = [[UIApplication sharedApplication] keyWindow];
        for (UIView * view in [window subviews]){
            [self globalResignFirstResponderRec:view];
        }
    }
    
    + (void) globalResignFirstResponderRec:(UIView*) view {
        if ([view respondsToSelector:@selector(resignFirstResponder)]){
            [view resignFirstResponder];
        }
        for (UIView * subview in [view subviews]){
            [self globalResignFirstResponderRec:subview];
        }
    }
    

    完美,除了你根本不需要globalResignFirstResponderRec方法,只需调用[view endEditing:YES]即可。它做的事情是一样的。 - n13
    耶!最终解决方案!我尝试了很多种方法,只有这个对我有效!非常感谢你! - douyw

    6

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

    真是太棒了!

    非常感谢。

    考虑到你们和其他人在这个帖子中提供的建议,我做了以下修改:

    在使用时的效果:

    [[self appDelegate] dismissKeyboard]; (注意:我添加了appDelegate作为NSObject的附加内容,这样我就可以在任何地方使用它)

    在内部实现上是这样的:

    - (void)dismissKeyboard 
    {
        UITextField *tempTextField = [[[UITextField alloc] initWithFrame:CGRectZero] autorelease];
        tempTextField.enabled = NO;
        [myRootViewController.view addSubview:tempTextField];
        [tempTextField becomeFirstResponder];
        [tempTextField resignFirstResponder];
        [tempTextField removeFromSuperview];
    }
    

    编辑

    我修改了我的答案,包括tempTextField.enabled = NO;。禁用文本字段将防止UIKeyboardWillShowNotificationUIKeyboardWillHideNotification键盘通知在您的应用程序中发送,如果您依赖这些通知。


    我不知道它是否有效,但它似乎是一个聪明而简单的方式。 - demon

    5

    以下是我在代码中使用的内容,它非常有效!

    在你的视图控制器.h文件中添加:

    @property (nonatomic) UITapGestureRecognizer *tapRecognizer;

    现在,在.m文件中的ViewDidLoad函数中添加以下内容:

    - (void)viewDidLoad {
        //Keyboard stuff
        tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTapAnywhere:)];
        tapRecognizer.cancelsTouchesInView = NO;
        [self.view addGestureRecognizer:tapRecognizer];
    }
    

    此外,在.m文件中添加此功能:
    - (void)handleSingleTap:(UITapGestureRecognizer *) sender
    {
        [self.view endEditing:YES];
    }
    

    谢谢!但你应该将函数重命名为“didTapAnywhere”或手势识别器重命名为“handleSingleTap”;-) - Matthijs

    5
    比Meagar的答案更简单
    重写touchesBegan:withEvent:方法
    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
        [textField resignFirstResponder];`
    }
    

    当您在背景的任何地方触摸时,这将隐藏键盘


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