问题描述
我开发的iPad应用程序有大量的UIViewControllers,UITableViews(与UITextFields的accessoryViews的单元格)等等。许多UIViewController出现在导航层次结构。
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内发生触摸。所以,我需要添加一些代码调用 endEditing
当一个UITableView被触摸等等,这意味着我的应用程序将自由地洒上大量的代码来关闭
...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];
}
我们不需要对第一个响应者做任何事情第一响应者。如果有第一个响应者,它包含一个触摸,我们不想强制它辞职。 (记住,可以同时有多个触摸!)如果有第一个响应者,并且新的触摸出现在另一个视图中,可以成为第一个响应者,系统将自动正确地处理它,所以我们也想忽略这种情况。但是如果有第一个响应者,并且它不包含任何触摸,并且新的触摸出现在不能成为第一个响应者的视图中,我们要让第一个响应者辞职。 / p>
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
.
如果您创建在
应用程序:didFinishLaunchingWithOptions:
中,您需要 #importMyWindow.h code>顶部的
,然后更改
应用程序:didFinishLaunchingWithOptions:
以创建而不是
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之外的任何位置触摸键盘的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!