在UITextField外面的任何位置轻触键盘

我正在开发一个iPad应用程序,它有大量的UIViewControllers,UITableViews(带有UITextFields的附件的单元格)等等。许多UIViewController出现在导航层次结构中。

UITextFields有很多不同的地方,包括UITableViewCell accessoryViews。

我想制定一个有效的策略来解除键盘,只要用户触摸外面当前正在编辑的UITextField。 我已经search了键盘解雇技术,但还没有find解释一般的键盘解雇策略如何工作的答案。

例如,我喜欢这种方法,将以下代码添加到任何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被触摸。

我想我可以尝试一下,找出所有需要拦截的地方以及键盘被解散的地方,但是在我看来可能有更好的devise模式来处理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:创build了UIWindow application:didFinishLaunchingWithOptions:需要在AppDelegate.m的顶部#import "MyWindow.h" ,然后更改application:didFinishLaunchingWithOptions:创build一个MyWindow而不是UIWindow

如果您要在笔尖中创buildUIWindow ,则需要将窗口的自定义类设置为笔尖中的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将帮助您find第一个共鸣

 @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. } 

通知function将是这样的

 - (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 ,这个button动作会隐藏键盘,

这里的一个限制是, UITextField必须在self.view但是你可以适应这种技术为您的需要进行一些修改,希望它可以帮助你

我其实很喜欢奥马尔的技巧 – 它的效果非常好 – 但我在这个例子中加了几行。

 - (void)keyboardDidShow:(NSNotification*)notification { UIButton *button = [[UIButton alloc] init]; CGRect rect = self.view.bounds; button.frame = rect; button.backgroundColor = [UIColor blackColor]; [button setAlpha:0.5f]; button.tag = 111; UIView *currentResponder = [self.view findFirstResponder]; [self.view bringSubviewToFront:currentResponder]; if (currentResponder == _descriptionTextView) [_descriptionTextView setTextColor:[UIColor whiteColor]]; [button addTarget:currentResponder action:@selector(resignFirstResponder) forControlEvents:UIControlEventTouchUpInside]; [self.view insertSubview:button belowSubview:currentResponder]; } 

我在他的解决scheme中添加了三行(参见上文):

  1. 将button的alpha设置为0.6,这样我就可以看到我的一些viewController。
  2. 把当前的响应器放在前面,这样我的其他视图都不在button前面。
  3. 切换我的UITextView的颜色,使文本更清晰(我的UITextView有一个清晰的背景,所以文字不显示清楚)。

无论如何,只有微小的变化,但希望这些帮助。 感谢奥马尔。

要辞职当前的第一响应者,我尝试过使用endEditing,但之后有一些问题设置了第一响应者。 然后,我使用recursion方法来查找UIView类别方法中的第一个响应者:

 - (UIView *)getFirstResponder { if (self.isFirstResponder) { return self; } for (UIView *subView in self.subviews) { UIView *firstResponder = [subView getFirstResponder]; if (firstResponder != nil) { return firstResponder; } } return nil; } 

一旦响应者返回,那么我可以在它上面调用resignFirstResponder。

就在今天,我工作的一个哥们给了我一个更好的办法: http : //overooped.com/post/28510603744/i-had-completely-forgotten-about-nil-targeted

只需调用这个:

 [[UIApplication sharedApplication] sendAction:@selector(resignFirstResponder) to:nil from:nil forEvent:nil]; 

真棒一class轮!

在Swift 3

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

可能是我不明白你想要什么,但- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch方法可能会帮助你解散键盘

将“textFieldDidChange”通知方法添加到文本字段控件。

[textField addTarget:self action:@selector(textFieldDidChange 🙂 forControlEvents:UIControlEventEditingDidEnd];

为我工作