iOS-触摸UITextField外部时关闭键盘


461

我想知道当用户触摸之外时如何使键盘消失UITextField


8
德米特里有正确的答案。这不是手势问题,而是辞职第一响应者的问题。Dmirty的回答也由马克,纳丁和LeMarche建议的解决方案开始iOS 4的开发,第4章,第83页
JWW

Answers:


760

您需要添加一个UITapGestureRecogniser并将其分配给视图,然后UITextField在其选择器上调用resign第一响应者。

编码:

在viewDidLoad中

UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(dismissKeyboard)];

[self.view addGestureRecognizer:tap];

在dismissKeyboard中:

-(void)dismissKeyboard 
{
    [aTextField resignFirstResponder];
}

(哪里aTextField是负责键盘的文本字段)

Swift 3版本看起来像这样

let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.dismissKeyboard (_:)))
self.view.addGestureRecognizer(tapGesture)

用于关闭键盘

func dismissKeyboard (_ sender: UITapGestureRecognizer) {
    aTextField.resignFirstResponder()
}

6
这行得通,但是它所做的是它也禁用或使导航栏按钮不可单击。那么有没有解决的办法?
Parth Bhatt

137
我认为比不需要文本字段更好,[aTextField resignFirstResponder]并且可以[self.view endEditing:YES];在多个文本字段中使用
朱利安·克罗尔(JulianKról)2014年

90
因此,无需定义任何方法,[self.view addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self.view action:@selector(endEditing:)]];
单线

8
我在[tap setCancelsTouchesInView:NO];@qiaoyi的答案中添加了@ParthBhatt;我有一个表不响应行选择的问题。5年后,我希望这对其他人有帮助。
汤姆·霍华德

7
迅捷的一线客: self.view.addGestureRecognizer(UITapGestureRecognizer(target: self.view, action: #selector(UIView.endEditing(_:))))
故障

176

我混了几个答案。

使用在执行期间初始化的ivar viewDidLoad:

UIGestureRecognizer *tapper;

- (void)viewDidLoad
{
    [super viewDidLoad];
    tapper = [[UITapGestureRecognizer alloc]
                initWithTarget:self action:@selector(handleSingleTap:)];
    tapper.cancelsTouchesInView = NO;
    [self.view addGestureRecognizer:tapper];
}

取消当前正在编辑的内容:

- (void)handleSingleTap:(UITapGestureRecognizer *) sender
{
    [self.view endEditing:YES];
}

太棒了,它可以在UITableView上使用!它拯救了我的一天!附带说明一下,我将使用[UITextField resignFirstResponder]而不是[UIView endEditing:],因为我有更多的UITextField,即使新的UITextField可见,endEditing的滚动位置也不正确。
Luuiji博士2012年

1
完全错误,因为从未将UITapGestureRecognizer添加到视图中!

1
这里的cancelsTouchesInView的目的是什么?
亚当·约翰斯

3
如果您不在此处添加cancelsTouchesInView = false,那么您将无法在视图中选择其他类型的项目。您的轻拍将被gestureRecognizer拦截。
HalR

104

检查一下,这将是最简单的方法,

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
      [self.view endEditing:YES];// this will do the trick
}

要么

该库将处理包括滚动条自动滚动,点击空间以隐藏键盘等。

https://github.com/michaeltyson/TPKeyboard避免


@Ramesh是的,但不是这样。您需要从UITableView创建一个子类,并且在该类内部实现touchesBegan方法,然后调用endEditing方法。有用!不要忘记使用新创建的类在笔尖中为表设置类。
2013年

3
当用户触摸视图上的其他控件时,它不会隐藏键盘。例如:我在UIView上有一个UIButton和UITextField。我点击UITextField,然后点击按钮,在这种情况下键盘不会隐藏。
雅各布·达姆

2
@JacobDam你是对的。目的是在aTextField 之外发生任何事情时,请resignfirstresponder 隐藏键盘。

88

我看到有些人在使用该UITapGestureRecognizer方法时遇到问题。在保持原样的现有按钮的点击行为不变的情况下实现此功能的最简单方法是,在@ Jensen2k的答案中仅添加一行:

[tap setCancelsTouchesInView:NO];

这使我现有的按钮仍然可以使用@Dmitry Sitnikov的方法来工作。

property在这里阅读有关内容(搜索CancelsTouchesInView):UIGestureRecognizer类参考

我不确定它如何与滚动条一起使用,因为我看到一些问题,但希望其他人可能会遇到与我相同的情况。


3
对于使用UIImageView,在视图的背面具有背景图像的用户,我还要添加,[imageView setUserInteractionEnabled:NO];然后单击“单击”,这使此方法有效。
Paul Peelen 2012年

55

最好使您UIView的实例成为实例UIControl(在界面生成器中),然后将其TouchUpInside事件连接到dismissKeyboard方法。该IBAction方法如下所示:

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

1
谢谢!这对我有用,而其他解决方案则没有。(UITapGestureRecognizer干扰了点击视图内部的按钮)。touchesEnded之所以无法正常运作,是因为UIScrollView响应链混乱了。)
jlstrecker 2011年

1
这很好用,但是必须添加到您想要关闭键盘的任何东西中。有没有办法在点击其他任何内容时自动让文本框退出第一响应者?
Isaac Overacker'2

4
您不需要使UIView成为UIControl。确保UIView启用了用户交互。
Sofi Software LLC 2012年

1
@SofiSoftwareLLC您的建议对我不起作用。即使选中了“启用用户交互”,我也必须使其成为UIControl。
2013年

1
使用Storyboard,我将UIView更改为UIControl(如果我做了一个,我会选择一个UIView子类)。它运作良好。我还没有遇到任何问题。iOS8。
ToddB

30

斯威夫特4

设置你的UIViewController这种扩展方法一旦如viewDidLoad

override func viewDidLoad() {
    super.viewDidLoad()
    self.setupHideKeyboardOnTap()
}

并且即使轻按键盘也会关闭键盘NavigationBar

import UIKit
extension UIViewController {
    /// Call this once to dismiss open keyboards by tapping anywhere in the view controller
    func setupHideKeyboardOnTap() {
        self.view.addGestureRecognizer(self.endEditingRecognizer())
        self.navigationController?.navigationBar.addGestureRecognizer(self.endEditingRecognizer())
    }

    /// Dismisses the keyboard from self.view
    private func endEditingRecognizer() -> UIGestureRecognizer {
        let tap = UITapGestureRecognizer(target: self.view, action: #selector(self.view.endEditing(_:)))
        tap.cancelsTouchesInView = false
        return tap
    }
}

2
兄弟,你钉了!这个答案是完美的,并且效果非常好,非常感谢。

28

Swift版本,可与其他元素(例如UIButtonUITextField)结合使用:

override func viewDidLoad() {
    super.viewDidLoad()

    let tapper = UITapGestureRecognizer(target: self, action:#selector(endEditing))
    tapper.cancelsTouchesInView = false
    view.addGestureRecognizer(tapper)
}

1
目标应该self而不是self.view
EI机长v2.0 2015年

1
目标应该是self.view。否则,以上代码将崩溃。
娜迦·麦列什

canclesTouchesInView是一个方便使用的属性。谢谢。
JonSlowCN

旧习难改。那分号在这里做什么?;)
Alexandre G.

@AlexandreG。确实,尤其是因为我通常在编码c#。仅供参考,分号是允许使用语法的,只是没有必要。
罗布

17

怎么样:我知道这是一个旧帖子。它可能会帮助某人:)

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {  
    NSArray *subviews = [self.view subviews];
    for (id objects in subviews) {
        if ([objects isKindOfClass:[UITextField class]]) {
            UITextField *theTextField = objects;
            if ([objects isFirstResponder]) {
                [theTextField resignFirstResponder];
            }
        } 
    }
}

我没有检查UITextField,所以它适用于任何输入类型。没有看到任何问题。
弗兰克,

15

这是一个很好的通用解决方案:

目标C:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [self.view endEditing:YES];    
}

迅速:

override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
    self.view.endEditing(true)
}

基于@icodebuster解决方案:https ://stackoverflow.com/a/18756253/417652


12

斯威夫特4 oneliner

view.addGestureRecognizer(UITapGestureRecognizer(target: view, action: #selector(UIView.endEditing(_:))))

好的代码,但是该代码禁用了删除功能(-)
schellingerht

你是个天才!
Abhishek Bedi

10

我认为最简单(也是最好)的方法是对全局视图进行子类化,并使用hitTest:withEvent方法来监听任何触摸。键盘上的触摸未注册,因此仅当您在其他地方触摸/滚动/滑动/捏...时才调用hitTest:withEvent [self endEditing:YES]

这比使用更好,touchesBegan因为touchesBegan如果单击视图顶部的按钮不会被调用。例如,它比UITapGestureRecognizer不能识别滚动手势要好。它也比使用暗屏更好,因为在复杂而动态的用户界面中,无法将暗屏无处不在。此外,它不会阻止其他操作,您无需点按两次即可选择外部的按钮(例如在的情况下UIPopover)。

另外,它比调用更好[textField resignFirstResponder],因为屏幕上可能有许多文本字段,所以这对所有这些都有效。


不幸的是,这似乎干扰了UISearchBar的“ X”以清除文本。
huggie 2012年

1
这种方法存在一些问题。例如,您不能滚动滚动视图而不将焦点放在文本字段上。不建议用于更复杂的场景...就我个人而言,我更喜欢Dmitry的uicontrol版本,也许可以通过Sofi Software的评论加以扩展。
katzenhut

滚动应用程序的一个示例是电子邮件应用程序,当您在搜索栏中键入内容然后滚动邮件列表时,将关闭键盘。
Enzo Tran 2013年

这是最好的方法,对我的其他项目也有帮助。
Chandramani


6

如果视图完全嵌入在中,UIScrollView则可以使用以下方法:

tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag;
tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeInteractive;

滚动表视图时,前者将在屏幕外为键盘设置动画,而后者将像股票Messages应用一样隐藏键盘。

请注意,这些功能在iOS 7.0或更高版本上可用。


6

您可以使用XCode 6及更高版本中的情节提要进行此操作:


创建动作以隐藏键盘

将此添加到您的ViewController使用的类的头文件中:

@interface TimeDelayViewController : UIViewController <UITextFieldDelegate>

- (IBAction)dissmissKeyboardOnTap:(id)sender;

@end

然后将其添加到同一ViewController的实现文件中:

- (IBAction)dissmissKeyboardOnTap:(id)sender{
    [[self view]endEditing:YES];
}

现在,这将是故事板场景(即ViewController)的“已接收动作”之一:

在此处输入图片说明


将动作与用户事件挂钩

现在,您需要将此操作与触发键盘的用户手势联系起来。

重要提示-您需要将情节提要中包含的“ UIView”转换为UIControl,以便它可以接收事件。从“视图控制器场景”层次结构中选择视图:

在此处输入图片说明

...并更改其类别:

在此处输入图片说明

现在,从场景的“已接收动作”旁边的小圆圈拖动到场景的“空”部分(实际上是将“已接收动作”拖动到UIControl)。系统将显示一系列事件,您可以将其关联到以下操作:

在此处输入图片说明

选择“内部润饰”选项。现在,您已经将创建的IBAction钩接到了用户触摸键盘的操作上。当用户点击键盘时,它将被隐藏。

(注意:要将动作与事件挂钩,也可以将接收到的动作直接拖到View Controllers层次结构中的UIControl上。在层次结构中显示为“ Control”。)


感谢您的解决方案和详细说明。对我真的有帮助。
永祥阮

5

如果我没错,您希望在外部轻按辞职键盘,textfield但您没有参考资料textfield

尝试这个;

  • 以全局textField命名 reftextField
  • 现在在textFieldDidBeginEditing设置引用文本字段为

    - (void) textFieldDidBeginEditing:(UITextField *)textField{
        reftextField = textField;
    }
  • 现在您可以愉快地在任何按钮时钟上使用(建议在开始编辑时添加透明按钮)

    - (void)dismissKeyboard {
          [reftextField resignFirstResponder];
    }
  • 或为完成辞职按钮尝试此操作。

    //for resigning on done button    
    - (BOOL) textFieldShouldReturn:(UITextField *)textField{
        [textField resignFirstResponder];
        return YES;
    }

3

只是为了在此处添加我的版本,该版本介绍了如何在外部触摸时关闭键盘。

viewDidLoad:

UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTap:)];
[self.view addGestureRecognizer:singleTap];

任何地方:

-(void)handleSingleTap:(UITapGestureRecognizer *)sender{
    [textFieldName resignFirstResponder];
    puts("Dismissed the keyboard");
}

3

这里有很多有关使用-的好答案UITapGestureRecognizer,所有这些都打破了UITextFieldclear(X)按钮。解决方案是通过其委托抑制手势识别器:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    BOOL touchViewIsButton = [touch.view isKindOfClass:[UIButton class]];
    BOOL touchSuperviewIsTextField = [[touch.view superview] isKindOfClass:[UITextField class]];
    return !(touchViewIsButton && touchSuperviewIsTextField);
}

这不是最可靠的解决方案,但对我有用。


确实,如果我在同一个文本字段中点击,上述所有操作都会失败。即使文本字段是我的响应者,它也辞职。它也关闭键盘轻敲文本字段内清除按钮
Mrug

我可以通过仅测试button:来简化它return !touchViewIsButton。我猜想其他任何需要关闭键盘的按钮也应该在其操作中这样做。此外,在键盘关闭时更改布局时可能不会调用TouchUpInside。
玛丽安·塞尔尼

3

您可以如下创建UiView的类别并覆盖touchesBegan肉类。

它对我来说很好用,并且是针对此问题的集中解决方案。

#import "UIView+Keyboard.h"
@implementation UIView(Keyboard)

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self.window endEditing:true];
    [super touchesBegan:touches withEvent:event];
}
@end

3

@ Jensen2k的Swift版本的答案:

let gestureRecognizer : UITapGestureRecognizer = UITapGestureRecognizer.init(target: self, action: "dismissKeyboard")
self.view.addGestureRecognizer(gestureRecognizer)

func dismissKeyboard() {
    aTextField.resignFirstResponder()
}

一支班轮

self.view.addTapGesture(UITapGestureRecognizer.init(target: self, action: "endEditing:"))

2

我以Barry为例进行新开发。效果很好!但是我必须稍作改动,要求仅关闭正在编辑的文本字段的键盘。

因此,我在Barry示例中添加了以下内容:

- (void) textFieldDidBeginEditing:(UITextField *)textField
{
    _textBeingEdited = textField;
}
-(void) textFieldDidEndEditing:(UITextField *)textField
{
    _textBeingEdited = nil;
}

另外,我更改了hideKeyboard方法,如下所示:

- (IBAction)hideKeyboard:(id)sender
{
    // Just call resignFirstResponder on all UITextFields and UITextViews in this VC
    // Why? Because it works and checking which one was last active gets messy.
    //UITextField * tf = (UITextField *) sender;
    [_textBeingEdited resignFirstResponder];
}

2

最简单,最快捷的方法之一就是将此代码添加到您的 viewDidLoad

[self.view addGestureRecognizer:[[UITapGestureRecognizer alloc]
                                     initWithTarget:self.view
                                     action:@selector(endEditing:)]];

2
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {

    if let touch = touches.first{
     view.endEditing(true)

     }
}

2

我在这里尝试了许多回复,但没有运气。UIButtons即使cancelsTouchesInView将手势识别器的属性设置为NO ,我的轻击手势识别器也总是导致我在点击时不响应。

这是最终解决问题的方法:

拥有一个ivar:

UITapGestureRecognizer *_keyboardDismissGestureRecognizer;

当文本字段开始编辑时,请设置手势识别器:

- (void) textFieldDidBeginEditing:(UITextField *)textField
{
    if(_keyboardDismissGestureRecognizer == nil)
    {
        _keyboardDismissGestureRecognizer = [[[UITapGestureRecognizer alloc]
                                       initWithTarget:self
                                       action:@selector(dismissKeyboard)] autorelease];
        _keyboardDismissGestureRecognizer.cancelsTouchesInView = NO;

        [self.view addGestureRecognizer:_keyboardDismissGestureRecognizer];
    }
}

然后,诀窍在于如何设置dismissKeyboard方法:

- (void) dismissKeyboard
{
    [self performSelector:@selector(dismissKeyboardSelector) withObject:nil afterDelay:0.01];
}

- (void) dismissKeyboardSelector
{
    [self.view endEditing:YES];

    [self.view removeGestureRecognizer:_keyboardDismissGestureRecognizer];
    _keyboardDismissGestureRecognizer = nil;
}

我想这只是将dismissKeyboardSelector执行从触摸处理执行堆栈中移出的事情...



1

这有效

在此示例中,aTextField是唯一的UITextField。

// YourViewController.h
// ...
@interface YourViewController : UIViewController /* some subclass of UIViewController */ <UITextFieldDelegate> // <-- add this protocol
// ...
@end

// YourViewController.m

@interface YourViewController ()
@property (nonatomic, strong, readonly) UITapGestureRecognizer *singleTapRecognizer;
@end
// ...

@implementation
@synthesize singleTapRecognizer = _singleTapRecognizer;
// ...

- (void)viewDidLoad
{
    [super viewDidLoad];
    // your other init code here
    [self.view addGestureRecognizer:self.singleTapRecognizer];

{

- (UITapGestureRecognizer *)singleTapRecognizer
{
    if (nil == _singleTapRecognizer) {
        _singleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(singleTapToDismissKeyboard:)];
        _singleTapRecognizer.cancelsTouchesInView = NO; // absolutely required, otherwise "tap" eats events.
    }
    return _singleTapRecognizer;
}

// Something inside this VC's view was tapped (except the navbar/toolbar)
- (void)singleTapToDismissKeyboard:(UITapGestureRecognizer *)sender
{
    NSLog(@"singleTap");
    [self hideKeyboard:sender];
}

// When the "Return" key is pressed on the on-screen keyboard, hide the keyboard.
// for protocol UITextFieldDelegate
- (BOOL)textFieldShouldReturn:(UITextField*)textField
{
    NSLog(@"Return pressed");
    [self hideKeyboard:textField];
    return YES;
}

- (IBAction)hideKeyboard:(id)sender
{
    // Just call resignFirstResponder on all UITextFields and UITextViews in this VC
    // Why? Because it works and checking which one was last active gets messy.
    [aTextField resignFirstResponder];
    NSLog(@"keyboard hidden");
}

1
- (void)viewDidLoad
{
    [super viewDidLoad]; 

UITapGestureRecognizer *singleTapGestureRecognizer = [[UITapGestureRecognizer alloc]
                                                          initWithTarget:self
                                                          action:@selector(handleSingleTap:)];
    [singleTapGestureRecognizer setNumberOfTapsRequired:1];
    [singleTapGestureRecognizer requireGestureRecognizerToFail:singleTapGestureRecognizer];

    [self.view addGestureRecognizer:singleTapGestureRecognizer];
}

- (void)handleSingleTap:(UITapGestureRecognizer *)recognizer
{
    [self.view endEditing:YES];
    [textField resignFirstResponder];
    [scrollView setContentOffset:CGPointMake(0, -40) animated:YES];

}

1

将此代码添加到您的ViewController.m文件中:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self.view endEditing:YES];
}

1

在这种情况下,可以使用ScrollView并将其添加到TextFieldScrollView中,我想要点击ScrollView和,然后关闭键盘。我试图创建示例代码以防万一。像这样,

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var scrollView: UIScrollView!
    @IBOutlet weak var textField: UITextField!

    override func viewDidLoad() {
        super.viewDidLoad()

        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(ViewController.tap(_:)))
        view.addGestureRecognizer(tapGesture)
        // Do any additional setup after loading the view, typically from a nib.
    }
    func tap(gesture: UITapGestureRecognizer) {
        textField.resignFirstResponder()
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

您的情节提要板就这样看。

在此处输入图片说明


1

您可以通过在UITextField外部单击来使用UITapGestureRecongnizer方法关闭键盘。通过使用此方法,每当用户在UITextField之外单击时,键盘将被关闭。下面是使用它的代码片段。

 UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]
                                   initWithTarget:self
                                   action:@selector(dismissk)];

    [self.view addGestureRecognizer:tap];


//Method
- (void) dismissk
{
    [abctextfield resignFirstResponder];
    [deftextfield resignFirstResponder];

}

1
  • 在视图中设置文本字段委托确实加载了:

    override func viewDidLoad() 
    {
      super.viewDidLoad()
      self.userText.delegate = self
    }
  • 添加此功能:

    func textFieldShouldReturn(userText: UITextField!) -> Bool 
    {
     userText.resignFirstResponder()
     return true;
    }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.