为什么每次选择另一个TextField时都会调用UIKeyboardWillShowNotification?

我有一个项目,其中包含一个UIScrollView和许多UITextField

我第一次选择UITextFieldUIKeyboardWillShowNotification ,这很好。 但每当我选择新的UITextField (键盘UIKeyboardWillShowNotification那里)时,再次调用UIKeyboardWillShowNotification !!!,这很奇怪。

我还为[UIResponder resignFirstResponder]设置了一个符号断点,我看到它在[UIResponder resignFirstResponder]之前和之后被击中!!!

另一件事是,当我点击键盘上的“完成”按钮时,才会调用UIKeyboardWillHideNotification

我肯定不会在任何地方调用任何resignFirstResponderbecomeFirstResponderendEditing 。 (我的意思是不要错误地打电话)

什么可能导致这个问题?

这是堆栈跟踪 在此处输入图像描述

问题是我为UITextField设置了inputAccessoryView ,这会导致在选择新的UITextField时再次调用UIKeyboardWillShowNotification

这篇文章在iOS上使用键盘解释了这一点

当我们将外部键盘连接到iPad时,会发生其他更改。 在这种特殊情况下,通知行为取决于控件的inputAccessoryView属性,这是显示键盘的原因。

如果inputAccessoryView不存在或其高度等于0磅,则不会发送键盘通知。 我的猜测是,这是因为在这种情况下,应用程序中不会发生视觉变化。 否则,所有通知都按预期运行 – 这意味着当键盘显示或隐藏在正常(未取消停靠或拆分)状态时,大多数情况下都会发送它们。

每当选择新的UITextField ,操作系统需要再次计算键盘的帧,并发布以下通知

 UIKeyboardWillChangeFrameNotification UIKeyboardWillShowNotification UIKeyboardDidChangeFrameNotification UIKeyboardDidShowNotification 

当TextField失去其第一个响应者状态时,这同样适用

请注意,对inputAccessoryView使用相同的 View将导致UIKeyboardWillShowNotification仅调用一次

为解决此问题,我使用以下代码取消UIKeyboardWillShowNotification回调,如果键盘的框架没有更改。

 func keyboardWillShow(notification: NSNotification) { let beginFrame = notification.userInfo![UIKeyboardFrameBeginUserInfoKey]!.CGRectValue() let endFrame = notification.userInfo![UIKeyboardFrameEndUserInfoKey]!.CGRectValue() // Return early if the keyboard's frame isn't changing. guard CGRectEqualToRect(beginFrame, endFrame) == false else { return } ... } 

对于Swift 3/4:

 func keyboardWillShow(notification: Notification) { let userInfo = notification.userInfo! let beginFrameValue = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)! let beginFrame = beginFrameValue.cgRectValue let endFrameValue = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)! let endFrame = endFrameValue.cgRectValue if beginFrame.equalTo(endFrame) { return } // Do something with 'will show' event ... } 

一般来说,我发现许多事情都可能导致虚假的UIKeyboardWillShowUIKeyboardWillHide通知。 我的解决方案是使用属性来跟踪键盘是否已经显示:

 func keyboardShow(_ n:Notification) { if self.keyboardShowing { return } self.keyboardShowing = true // ... other stuff } func keyboardHide(_ n:Notification) { if !self.keyboardShowing { return } self.keyboardShowing = false // ... other stuff } 

那些警卫完全阻止虚假通知,之后一切都很好。 而keyboardShowing属性可能因其他原因而有用,因此无论如何它都值得跟踪。

最好的方法是添加通知并在您的目的解决后将其删除。

喜欢这个 。

 - (void)viewWillAppear:(BOOL)animated { // register for keyboard notifications [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow) name:UIKeyboardWillShowNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide) name:UIKeyboardWillHideNotification object:nil]; } 

现在在keyboardWillShow编写用于移动视图和textField的代码,并将它们恢复到keyboardWillHide方法中的位置。

同时删除观察者

 - (void)viewWillDisappear:(BOOL)animated { // unregister for keyboard notifications while not visible. [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil]; } 

您还可以在按return键时退出响应者。

 -(BOOL)textFieldShouldReturn:(UITextField *)textField { [_txtFieldEmail resignFirstResponder]; [_txtFieldPassword resignFirstResponder]; return YES; } 

这应该可以解决你的问题。

对于那些不使用inputAccessoryView但仍有问题的人,可能是由于使用了敏感(密码)字段。 通过UIKeyboardWillShowNotification查看此Stackoverflowpost并回答: IOS8中的keyboardWillShow

经过半天的搜索和实验,我一直在努力解决这个问题,我认为这是最短,最可靠的代码。 它是许多答案的混合,其中大多数我忘记了我发现的地方(这里提到了部分内容)。

我的问题是WKWebView(当用户更改字段时)会产生一堆WillShow,WillHide等通知。 另外我还有外接键盘的问题,它仍然有屏幕触摸条的东西。

此解决方案使用相同的动画代码来“打开”和“关闭”键盘,它还将处理附加的外部键盘和自定义键盘视图。

首先注册UIKeyboardWillChangeFrameNotification。

 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil]; 

然后,您只需将更改映射到视图中(无论您更改高度或底部约束常量)。

 - (void)keyboardWillChangeFrame:(NSNotification *)notification { NSDictionary *userInfo = notification.userInfo; CGRect keyboardEnd = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue]; CGRect convertedEnd = [self.view convertRect:keyboardEnd fromView:nil]; // Convert the Keyboard Animation to an Option, note the << 16 in the option UIViewAnimationCurve keyAnimation = [userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue]; // Change the Height or Y Contraint to the new value. self.keyboardHeightConstraint.constant = self.view.bounds.size.height - convertedEnd.origin.y; [UIView animateWithDuration:[userInfo[UIKeyboardAnimationDurationUserInfoKey] floatValue] delay:0.0 options:keyAnimation << 16 animations:^{ [self.view layoutIfNeeded]; } completion:nil]; } 

动画到选项转换似乎有效(我只能找到它的使用示例,而不是如何/为什么),但是,我不相信它会保持这种状态,所以使用“股票”选项可能是明智的。 似乎键盘使用了一些没有指定的动画。