iOS - 在 UITextField 之外触摸时关闭键盘

Posted

技术标签:

【中文标题】iOS - 在 UITextField 之外触摸时关闭键盘【英文标题】:iOS - Dismiss keyboard when touching outside of UITextField 【发布时间】:2011-07-15 10:37:12 【问题描述】:

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

【问题讨论】:

德米特里有正确的答案。这不是手势问题——它是让第一响应者辞职的问题。 Dmirty 的回答也是 Mark、Nutting 和 LeMarche 在Beginning ios 4 Development,第 4 章,第 83 页中推荐的解决方案。 【参考方案1】:

您需要添加 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)

对于dismissKeyboard

@objc func dismissKeyboard (_ sender: UITapGestureRecognizer) 
    aTextField.resignFirstResponder()

【讨论】:

这行得通,但它的作用是它还禁用导航栏按钮或使导航栏按钮不可点击。那么有没有办法解决这个问题? 我认为比[aTextField resignFirstResponder] 更好的是[self.view endEditing:YES]; 它不需要对文本字段的引用,并且适用于多个文本字段 因此,无需定义任何方法,单行代码将是:[self.view addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self.view action:@selector(endEditing:)]]; @ParthBhatt 我根据@qiaoyi 的回答添加了[tap setCancelsTouchesInView:NO];;我遇到了一个表格不响应行选择的问题。 5 年后,我希望这对其他人有所帮助。 Swift 单行:self.view.addGestureRecognizer(UITapGestureRecognizer(target: self.view, action: #selector(UIView.endEditing(_:))))【参考方案2】:

我混合了几个答案。

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

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 并且 endEditing 给出了错误的滚动位置,即使新的 UITextField 可见。 大错特错,因为 UITapGestureRecognizer 从未添加到视图中! 这里cancelsTouchesInView的目的是什么? 如果你不在这里添加cancelsTouchesInView = false,那么你将无法在你的视图中选择其他类型的项目。你的点击会被gestureRecognizer拦截。【参考方案3】:

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

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

或者

这个库将处理包括滚动条自动滚动、点击空格来隐藏键盘等...

https://github.com/michaeltyson/TPKeyboardAvoiding

【讨论】:

@Ramesh 是的,但不是这样。您需要从 UITableView 创建一个子类,并在该类中实现 touchesBegan 方法,然后调用 endEditing 方法。有用!不要忘记使用新创建的类在 nib 中为您的表设置类。 当用户触摸视图中的其他控件时,它不会隐藏键盘。例如:我在 UIView 上有一个 UIButton 和 UITextField。我点击 UITextField,然后点击按钮,在这种情况下键盘不会隐藏。 @JacobDam 你是对的。如果 anything 在 aTextField 之外发生,目标是 resignfirstresponder 隐藏键盘。【参考方案4】:

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

[tap setCancelsTouchesInView:NO];

这使我现有的按钮在不使用@Dmitry Sitnikov 的方法的情况下仍然可以工作。

在此处阅读property(搜索CancelsTouchesInView):UIGestureRecognizer Class Reference

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

【讨论】:

我还想添加,对于那些使用UIImageView 在视图后面有背景图像的人,添加[imageView setUserInteractionEnabled:NO]; 并且您“点击”它,这使得这种方法有效。 【参考方案5】:

最好让您的UIView 成为UIControl 的实例(在界面生成器中),然后将它们的TouchUpInside 事件连接到dismissKeyboard 方法。这个IBAction 方法看起来像:

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

【讨论】:

谢谢!这对我有用,而其他解决方案没有。 (UITapGestureRecognizer 干扰了点击视图内的按钮)。 touchesEnded 不起作用,因为有一个 UIScrollView 与响应者链混淆。) 这很好用,但必须添加到您想要关闭键盘的任何内容中。有没有办法让文本框在点击其他任何内容时自动让第一响应者辞职? 您不需要将 UIView 设置为 UIControl。确保 UIView 已启用用户交互。 @SofiSoftwareLLC 您的建议对我不起作用。即使选中了“启用用户交互”,我也必须将其设为 UIControl。 使用 StoryBoard,我将 UIView 更改为 UIControl(如果我做了一个,我会选择 UIView 子类)。它运作良好。我没有遇到任何问题。 iOS8.【参考方案6】:

斯威夫特 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
    

【讨论】:

兄弟,你成功了!!这个答案很完美,效果很好,非常感谢。 谢谢!。这是拯救我的一天【参考方案7】:

Swift 版本,可与其他元素结合使用(如 UIButton 或另一个 UITextField):

override func viewDidLoad() 
    super.viewDidLoad()

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

【讨论】:

目标应该是self 而不是self.view 目标应该是 self.view。否则,上面的代码会崩溃。 canclesTouchesInView 是一个方便使用的属性。谢谢。 旧习惯难改。那个分号在这里做什么? ;) @AlexandreG。确实,特别是因为我通常在编码c#。仅供参考,分号是语法允许的,只是没有必要。【参考方案8】:

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

目标-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://***.com/a/18756253/417652

【讨论】:

【参考方案9】:

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

- (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 所以它适用于任何输入类型。没有发现任何问题。【参考方案10】:

Swift 4 oneliner

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

【讨论】:

不错的代码,但是这个禁用了删除功能(-) 你是个天才!【参考方案11】:

我认为最简单(也是最好)的方法是对全局视图进行子类化并使用hitTest:withEvent 方法来聆听任何触摸。键盘上的触摸未注册,因此仅当您 touch/scroll/swipe/pinch... 时调用 hitTest:withEvent,然后调用 [self endEditing:YES]

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

另外,它比调用[textField resignFirstResponder] 更好,因为屏幕上可能有很多文本字段,所以这适用于所有文本字段。

【讨论】:

不幸的是,这似乎会干扰 UISearchBar 的“X”清除文本。 这种方法存在一些问题。例如,您不能滚动滚动视图而不失去对文本字段的关注。不建议用于更复杂的场景……我个人更喜欢 Dmitry 的 uicontrol-version,可能会通过 Sofi Software 的评论进行扩展。 滚动的一个例子是在电子邮件应用程序中,当您在搜索栏中键入内容然后滚动邮件列表时,键盘将被关闭。 这是最好的方法,对我的其他项目也是如此。【参考方案12】:

这一定是通过触摸外部来隐藏键盘的最简单方法:

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

(来自How to dismiss keyboard when user tap other area outside textfield?)

【讨论】:

【参考方案13】:

如果视图完全嵌入在 UIScrollView 中,那么您可以使用以下内容:

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

当滚动表格视图时,前者将在屏幕外显示键盘动画,而后者将像股票消息应用程序一样隐藏键盘。

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

【讨论】:

【参考方案14】:

您可以使用 XCode 6 及更高版本中的 Storyboard 执行此操作:

创建隐藏键盘的动作

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

@interface TimeDelayViewController : UIViewController <UITextFieldDelegate>

- (IBAction)dissmissKeyboardOnTap:(id)sender;

@end

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

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

这现在将成为情节提要场景(即 ViewController)的“接收到的操作”之一:

将操作与用户事件挂钩

现在您需要将此操作与触摸键盘的用户手势挂钩。

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

...并更改其类别:

现在从场景的“接收到的动作”旁边的小圆圈拖动到场景的“空白”部分(实际上您正在将“接收到的动作”拖动到 UIControl)。您将看到一系列事件,您可以将这些事件关联到:

选择“内部修饰”选项。您现在已将您创建的 IBAction 与触发键盘的用户操作挂钩。当用户点击键盘时,它现在将被隐藏。

(注意:要将动作挂钩到事件,您还可以从接收到的动作直接拖动到视图控制器层次结构中的 UIControl。它在层次结构中显示为“控件”。)

【讨论】:

感谢您的解决方案和详细解释。对我真的很有帮助。【参考方案15】:

如果我没听错的话,你想通过点击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;

【讨论】:

【参考方案16】:

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

viewDidLoad:

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

任何地方:

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

【讨论】:

【参考方案17】:

这里有很多关于使用 UITapGestureRecognizer 的好答案——所有这些都破坏了 UITextField 的清除 (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);

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

【讨论】:

没错,如果我在同一个文本字段中点击,上述所有操作都会失败。即使文本字段是我的响应者,它也会辞职。它还会在点击 textField 内的清除按钮时关闭键盘 我会通过对按钮进行测试来简化它:return !touchViewIsButton。我想任何其他需要关闭键盘的按钮都应该在他们的动作中做到这一点。此外,当键盘关闭时布局发生变化时,可能不会调用 TouchUpInside。【参考方案18】:

您可以为 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

【讨论】:

【参考方案19】:

@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:"))

【讨论】:

【参考方案20】:

在 swift 5 中,您可以使用以下代码关闭文本字段外的键盘

override func viewDidLoad() 
    // ... code

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


@objc func dismissKeyboard(_ sender: UITapGestureRecognizer) 
    self.view.endEditing(true)

【讨论】:

如果您的视图中还包含 tableViewscollectionViews,请在 tapGesture 配置中包含以下内容:tapGesture.cancelsTouchesInView = false【参考方案21】:

目标-C:

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

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

    [self.view endEditing:YES];

斯威夫特:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) 
    view.endEditing(true)

【讨论】:

甜蜜!我喜欢这个答案。精确且尽可能小。为什么要设置手势识别器和所有其他样板?这应该是公认的答案【参考方案22】:

我在我的新开发中使用了 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];

【讨论】:

【参考方案23】:

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

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

【讨论】:

【参考方案24】:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) 

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

     

【讨论】:

【参考方案25】:

我在这里尝试了很多回复,但都没有运气。我的点击手势识别器总是导致我的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 执行从触摸处理执行堆栈中取出...

【讨论】:

【参考方案26】:

将消息resignFirstResponder 发送到将其放在那里的文本文件。请see this post了解更多信息。

【讨论】:

【参考方案27】:

这行得通

在这个例子中,aTextField 是唯一的 UITextField....如果还有其他 UITextViews,还有一点点要做。

// 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");

【讨论】:

【参考方案28】:
- (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];


【讨论】:

【参考方案29】:

在这种情况下,可以使用 ScrollView 并在 ScrollView 中添加到 TextField,我想点击 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.
    

你的故事板看起来就像。

【讨论】:

【参考方案30】:

您可以使用 UITapGestureRecongnizer 方法通过单击 UITextField 外部来关闭键盘。通过使用此方法,只要用户在 UITextField 外部单击,键盘就会被关闭。下面是使用它的代码sn-p。

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

    [self.view addGestureRecognizer:tap];


//Method
- (void) dismissk

    [abctextfield resignFirstResponder];
    [deftextfield resignFirstResponder];


【讨论】:

以上是关于iOS - 在 UITextField 之外触摸时关闭键盘的主要内容,如果未能解决你的问题,请参考以下文章

触摸 UITextField 时设置 UIPickerView 的默认值

触摸时 UITextField 崩溃

iOS:响应者辞职时,文本从子类 UITextfield 中消失

UITextField 添加到 UIView 时不响应触摸事件

UIImageView 的 UITextField 子视图没有响应触摸事件

UITextField 触摸事件在 UIScrollView 中不起作用