在UITextField外部的任何位置轻触键盘
我正在开发一个具有大量UIViewControllers,UITableViews(带有UITextFields的附件视图的单元)的iPad应用程序等等。许多UIViewControllers出现在导航层次结构中。
UITextField出现的地方很多,包括UITableViewCell accessoryViews。
我想制定一种有效的策略,只要用户触摸到当前正在编辑的UITextField外部就会关闭键盘。 我搜索了键盘解雇技术,但还没有找到解释一般键盘解雇策略如何工作的答案。
例如,我喜欢这种方法,将以下代码添加到任何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被触摸等等,这意味着我的应用程序将被大量的代码来解散键盘时,各种其他UIElements被触摸。
我想我可以试着找出触摸需要被截取和键盘被解散的所有不同的地方,但是在我看来,可能有更好的设计模式来处理iOS键盘解散事件。
任何人都可以分享他们在这个问题上的经验,并推荐一种特定的技术来统一处理整个应用程序的键盘解雇?
非常感谢
您的视图层次结构位于UIWindow
内部。 UIWindow
负责将触发事件转发到其sendEvent:
方法中正确的视图。 让我们让UIWindow
的一个子类覆盖sendEvent:
@interface MyWindow : UIWindow
@end
如果有窗口,窗口将需要对当前第一响应者的引用。 您可能会决定也使用UITextView
,因此我们将观察来自文本字段和文本视图的通知。
@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;
}
}
窗口将在初始化时开始观察通知,并在解除分配时停止:
- (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:
正常发送事件。
- (void)sendEvent:(UIEvent *)event {
[self adjustFirstResponderForEvent:event];
[super sendEvent:event];
}
如果没有第一响应者,我们不需要对第一响应者做任何事情。 如果有第一响应者,并且它包含触摸,我们不希望强迫它辞职。 (请记住,同时可以有多个触摸!)如果有第一个响应者,并且在另一个视图中出现了新的触摸(可以成为第一个响应者),系统将自动正确处理,因此我们也想忽略该情况。 但是,如果有第一响应者,并且它不包含任何触摸,并且在不能成为第一响应者的视图中出现新的触摸,我们希望使第一响应者辞职。
- (void)adjustFirstResponderForEvent:(UIEvent *)event {
if (currentFirstResponder_
&& ![self eventContainsTouchInFirstResponder:event]
&& [self eventContainsNewTouchInNonresponder:event]) {
[currentFirstResponder_ resignFirstResponder];
}
}
报告事件是否在第一响应者中包含触摸很容易:
- (BOOL)eventContainsTouchInFirstResponder:(UIEvent *)event {
for (UITouch *touch in [event touchesForWindow:self]) {
if (touch.view == currentFirstResponder_)
return YES;
}
return NO;
}
报告事件是否包含不能成为第一响应者的视图中的新触摸几乎一样简单:
- (BOOL)eventContainsNewTouchInNonresponder:(UIEvent *)event {
for (UITouch *touch in [event touchesForWindow:self]) {
if (touch.phase == UITouchPhaseBegan && ![touch.view canBecomeFirstResponder])
return YES;
}
return NO;
}
@end
一旦你实现了这个类,你需要改变你的应用程序来使用它,而不是UIWindow
。
如果您在application:didFinishLaunchingWithOptions:
创建了UIWindow
application:didFinishLaunchingWithOptions:
需要在AppDelegate.m
的顶部#import "MyWindow.h"
,然后更改application:didFinishLaunchingWithOptions:
创建一个MyWindow
而不是UIWindow
。
如果您要在笔尖中创建UIWindow
,则需要将该窗口的自定义类设置为笔尖中的MyWindow
。
这是一个更容易和有效的方式来处理。 这是你的视图控制器中的任何UITextField的工作。 你甚至可以将它添加到你的基本视图控制器(如果你有一个),它会像魅力一样工作。
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
if (![[touch view] isKindOfClass:[UITextField class]]) {
[self.view endEditing:YES];
}
[super touchesBegan:touches withEvent:event];
}
我通常使用以下内容
第一:
在你的实现文件中包含以下扩展, findFirstResponder
将帮助你找到第一个共鸣
@implementation UIView (FindFirstResponder)
- (UIView*)findFirstResponder
{
if (self.isFirstResponder) {
return self;
}
for (UIView *subView in self.subviews) {
UIView *responder = [subView findFirstResponder];
if (responder)
return responder;
}
return nil;
}
@end
然后在你的视图控制器viewDidLoad
添加以下内容
- (void)viewDidLoad
{
[super viewDidLoad];
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:@selector(keyboardDidShow:)
name:UIKeyboardDidShowNotification
object:nil];
[center addObserver:self selector:@selector(keyboardDidHide:)
name:UIKeyboardWillHideNotification
object:nil];
// Do any additional setup after loading the view, typically from a nib.
}
通知功能将是这样的
- (void) keyboardDidShow:(NSNotification*)notification
{
UIButton *button = [[UIButton alloc] init];
CGRect rect = self.view.bounds;
button.frame = rect;
button.backgroundColor = [UIColor blackColor];
button.tag = 111;
UIView *currentResponer = [self.view findFirstResponder];
[button addTarget:currentResponer action:@selector(resignFirstResponder) forControlEvents:UIControlEventTouchUpInside];
[self.view insertSubview:button belowSubview:currentResponer];
}
- (void) keyboardDidHide:(NSNotification*)notification
{
[[self.view viewWithTag:111] removeFromSuperview];
}
当键盘显示时,我在当前第一响应者下面添加一个UIButton
,这个按钮动作会隐藏键盘,
这里的一个限制是UITextField
必须在self.view
但是你可以根据你的需要进行一些修改来适应这种技术,希望它能帮助你
上一篇: Dismiss keyboard on touch anywhere outside UITextField
下一篇: Super slow lag/delay on initial keyboard animation of UITextField