在 UITextField 之外的任何地方关闭触摸键盘 [英] Dismiss keyboard on touch anywhere outside UITextField

查看:27
本文介绍了在 UITextField 之外的任何地方关闭触摸键盘的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个 iPad 应用程序,它具有大量 UIViewControllersUITableViews(带有 UITextFieldsaccessoryViews 的单元格) 等等.许多 UIViewControllers 出现在导航层次结构中.

I am developing an iPad app that has a large number of UIViewControllers, UITableViews (with cells with accessoryViews of UITextFields) etc, etc. Many of the UIViewControllers appear within a navigation hierarchy.

UITextFields 出现的地方有很多,包括 UITableViewCell accessoryViews.

There are many different places where UITextFields appear, including as UITableViewCell accessoryViews.

我想设计一种有效的策略,以便在用户触摸当前正在编辑的 UITextField 之外时关闭键盘.我搜索了键盘关闭技术,但还没有找到解释一般键盘关闭策略如何工作的答案.

I would like to devise an efficient strategy for dismissing the keyboard whenever the user touches outside the UITextField currently being edited. I have searched for keyboard dismiss techniques but have not yet found an answer that explains how a general keyboard dismiss strategy might work.

例如,我喜欢这种方法,将以下代码添加到任何 ViewController 中:

For example, I like this approach, where the following code is added to any ViewController:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    NSLog(@"* * * * * * * * *ViewControllerBase touchesBegan");

    [self.view endEditing:YES]; // dismiss the keyboard

    [super touchesBegan:touches withEvent:event];
}

...但是这种技术不处理例如在显示的 UITableView 中发生触摸的情况.因此,当 UITableView 被触摸等时,我需要添加一些代码来调用 endEditing.这意味着我的应用程序将大量散布大量代码以触摸其他各种 UIElements 时关闭键盘.

...but this technique does not deal with situations where, for example, a touch occurs within a UITableView which is on display. So, I'd need to add some code to call endEditing when a UITableView is touched etc, etc. Which means that my app will be liberally sprinkled with lots of code to dismiss the keyboard when various other UIElements are touched.

我想我可以尝试确定需要拦截触摸和关闭键盘的所有不同位置,但在我看来,可能有更好的设计模式来处理 iOS 键盘关闭事件.

I guess I could just try and identify all the different places where touches need to be intercepted and the keyboard dismissed, but it seems to me that there may be a better design pattern somewhere for handling iOS keyboard dismiss events.

谁能分享他们在这方面的经验,并推荐一种在整个应用程序中通用处理键盘关闭的特定技术?

Can anyone share their experiences in this matter, and recommend a specific technique for generically handling keyboard dismissal across an entire app?

非常感谢

推荐答案

您的视图层次结构位于 UIWindow 中.UIWindow 负责将触摸事件转发到其 sendEvent: 方法中的正确视图.让我们创建一个 UIWindow 的子类来覆盖 sendEvent:.

Your view hierarchy lives inside a UIWindow. The UIWindow is responsible for forwarding touch events to the correct view in its sendEvent: method. Let's make a subclass of UIWindow to override sendEvent:.

@interface MyWindow : UIWindow
@end

窗口将需要对当前第一响应者的引用(如果有).您可能还决定使用 UITextView,因此我们将观察来自文本字段和文本视图的通知.

The window will need a reference to the current first responder, if there is one. You might decide to also use UITextView, so we'll observe notifications from both text fields and text views.

@implementation MyWindow {
    UIView *currentFirstResponder_;
}

- (void)startObservingFirstResponder {
    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
    [center addObserver:self selector:@selector(observeBeginEditing:) name:UITextFieldTextDidBeginEditingNotification object:nil];
    [center addObserver:self selector:@selector(observeEndEditing:) name:UITextFieldTextDidEndEditingNotification object:nil];
    [center addObserver:self selector:@selector(observeBeginEditing:) name:UITextViewTextDidBeginEditingNotification object:nil];
    [center addObserver:self selector:@selector(observeEndEditing:) name:UITextViewTextDidEndEditingNotification object:nil];
}

- (void)stopObservingFirstResponder {
    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
    [center removeObserver:self name:UITextFieldTextDidBeginEditingNotification object:nil];
    [center removeObserver:self name:UITextFieldTextDidEndEditingNotification object:nil];
    [center removeObserver:self name:UITextViewTextDidBeginEditingNotification object:nil];
    [center removeObserver:self name:UITextViewTextDidEndEditingNotification object:nil];
}

- (void)observeBeginEditing:(NSNotification *)note {
    currentFirstResponder_ = note.object;
}

- (void)observeEndEditing:(NSNotification *)note {
    if (currentFirstResponder_ == note.object) {
        currentFirstResponder_ = nil;
    }
}

窗口将在初始化时开始观察通知,并在释放时停止:

The window will start observing the notifications when it's initialized, and stop when it's deallocated:

- (id)initWithCoder:(NSCoder *)aDecoder {
    if ((self = [super initWithCoder:aDecoder])) {
        [self commonInit];
    }
    return self;
}

- (id)initWithFrame:(CGRect)frame {
    if ((self = [super initWithFrame:frame])) {
        [self commonInit];
    }
    return self;
}

- (void)commonInit {
    [self startObservingFirstResponder];
}

- (void)dealloc {
    [self stopObservingFirstResponder];
}

我们将覆盖sendEvent:以根据事件调整"第一响应者,然后调用super的sendEvent:以正常发送事件.

We'll override sendEvent: to "adjust" the first responder based on the event, and then call super's sendEvent: to send the event normally.

- (void)sendEvent:(UIEvent *)event {
    [self adjustFirstResponderForEvent:event];
    [super sendEvent:event];
}

如果没有第一响应者,我们不需要对第一响应者做任何事情.如果有第一响应者,并且它包含触摸,我们不想强迫它辞职.(记住,可以同时有多个触摸!)如果有一个第一响应者,并且一个新的触摸出现在另一个可以成为第一响应者的视图中,系统会自动正确处理,所以我们也想忽略这种情况.但是如果有一个第一响应者,并且它不包含任何触摸,并且一个新的触摸出现在一个不能成为第一响应者的视图中,我们想让第一响应者辞职.

We don't need to do anything about the first responder if there is no first responder. If there is a first responder, and it contains a touch, we don't want to force it to resign. (Remember, there can be multiple touches simultaneously!) If there is a first responder, and a new touch appears in another view that can become the first responder, the system will handle that correctly automatically, so we also want to ignore that case. But if there is a first responder, and it doesn't contain any touches, and a new touch appears in a view that can't become first responder, we want to make the first responder resign.

- (void)adjustFirstResponderForEvent:(UIEvent *)event {
    if (currentFirstResponder_
        && ![self eventContainsTouchInFirstResponder:event]
        && [self eventContainsNewTouchInNonresponder:event]) {
        [currentFirstResponder_ resignFirstResponder];
    }
}

在第一响应者中报告事件是否包含触摸很容易:

Reporting whether an event contains a touch in the first responder is easy:

- (BOOL)eventContainsTouchInFirstResponder:(UIEvent *)event {
    for (UITouch *touch in [event touchesForWindow:self]) {
        if (touch.view == currentFirstResponder_)
            return YES;
    }
    return NO;
}

在无法成为第一响应者的视图中报告事件是否包含新触摸几乎同样简单:

Reporting whether an event contains a new touch in a view that can't become first responder is almost as easy:

- (BOOL)eventContainsNewTouchInNonresponder:(UIEvent *)event {
    for (UITouch *touch in [event touchesForWindow:self]) {
        if (touch.phase == UITouchPhaseBegan && ![touch.view canBecomeFirstResponder])
            return YES;
    }
    return NO;
}

@end

一旦你实现了这个类,你需要改变你的应用来使用它而不是 UIWindow.

Once you've implemented this class, you need to change your app to use it instead of UIWindow.

如果您在 application:didFinishLaunchingWithOptions: 中创建 UIWindow,则需要在顶部 #import "MyWindow.h"AppDelegate.m,然后更改 application:didFinishLaunchingWithOptions: 以创建 MyWindow 而不是 UIWindow.

If you're creating your UIWindow in application:didFinishLaunchingWithOptions:, you need to #import "MyWindow.h" at the top of your AppDelegate.m, and then change application:didFinishLaunchingWithOptions: to create a MyWindow instead of a UIWindow.

如果你在 nib 中创建你的 UIWindow,你需要在 nib 中将窗口的自定义类设置为 MyWindow.

If you're creating your UIWindow in a nib, you need to set the custom class of the window to MyWindow in the nib.

这篇关于在 UITextField 之外的任何地方关闭触摸键盘的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆