为啥每次选择另一个 TextField 时都会调用 KeyboardWillShowNotification?
Posted
技术标签:
【中文标题】为啥每次选择另一个 TextField 时都会调用 KeyboardWillShowNotification?【英文标题】:Why is UIKeyboardWillShowNotification called every time another TextField is selected?为什么每次选择另一个 TextField 时都会调用 KeyboardWillShowNotification? 【发布时间】:2014-03-21 04:03:49 【问题描述】:我有一个项目,其中包含一个 UIScrollView
和许多 UITextField
。
我第一次选择UITextField
,调用UIKeyboardWillShowNotification
,这很好。但是每当我选择新的UITextField
(键盘仍然在那里)时,UIKeyboardWillShowNotification
就会再次被调用!!!,这很奇怪。
我还为[UIResponder resignFirstResponder]
设置了一个符号断点,我看到它在UIKeyboardWillShowNotification
被调用之前和之后被命中!!!
另一件事是UIKeyboardWillHideNotification
仅在我点击键盘上的“完成”按钮时才被调用
我肯定不会在任何地方打电话给任何resignFirstResponder
、becomeFirstResponder
、endEditing
。 (我的意思是不要打错)
什么会导致这个问题?
这里是堆栈跟踪
【问题讨论】:
您为什么认为这是一个问题? @matt 刚刚编辑了,我的意思是键盘还在,但是为什么又调用了 UIKeyboardWillShowNotification 呢? 默认情况下,它不应该被调用两次,所以你的代码中必须有其他的东西来触发这个动作。如果您创建一个包含两个文本字段的项目并使用 UIKeyboardWillShowNotification 名称实现通知,则您的选择器只会被调用一次。 @Chris 谢谢,那么什么会导致这个问题呢?一些手势识别器,.. ? 你对手势识别器做了什么奇怪的事情吗?你对becomeFirstResponder
做了什么奇怪的事吗?
【参考方案1】:
为了解决这个问题,如果键盘的框架没有改变,我使用以下代码取消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
...
【讨论】:
更优雅!谢谢! 我相信beginFrame.equalTo(endFrame)
是swift 3的语法
感谢您的回答。这对我的情况真的很有帮助。
像魅力一样工作【参考方案2】:
问题是我为UITextField
设置了inputAccessoryView
,这导致在选择新的UITextField
时再次调用UIKeyboardWillShowNotification
这篇文章Working With Keyboard on ios很好地解释了这一点
当我们将外部键盘连接到 iPad。在这种特殊情况下,通知行为取决于 在控件的 inputAccessoryView 属性上,这是原因 用于显示键盘。
如果 inputAccessoryView 不存在或其高度等于 0 点,不发送键盘通知。我的猜测是,这是 因为在这种情况下,应用程序中不会发生视觉变化。 否则,所有通知都会按预期运行——这意味着它们是 在大多数情况下,在显示键盘时发送 或隐藏在正常(未取消停靠或拆分)状态。
Whenever new UITextField
is selected, the OS needs to compute the frame for the keyboard again, and the following notifications are posted
UIKeyboardWillChangeFrameNotification
UIKeyboardWillShowNotification
UIKeyboardDidChangeFrameNotification
UIKeyboardDidShowNotification
同样适用于 TextField 失去其第一响应者状态时
请注意,对inputAccessoryView
使用same 视图将导致UIKeyboardWillShowNotification
仅被调用一次
【讨论】:
你能帮我理解避免收到两次通知的解决方案吗,我的意思是在代码中要做什么? 在 iOS 9 下,我为我的新文本视图inputAccesoryView
重复使用相同的视图,并且每次新文本视图激活时都会触发 UIKeyboardWillChangeFrameNotification
和 UIKeyboardWillShowNotification
。在 iOS 8 下,这种情况下不会生成通知。【参考方案3】:
总的来说,我发现很多事情都会导致虚假的UIKeyboardWillShow
和UIKeyboardWillHide
通知。我的解决方案是使用一个属性来跟踪键盘是否已经显示:
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
属性可能出于其他原因有用,因此无论如何它都值得跟踪。
【讨论】:
不错的答案。非常简单。但是,如果应用程序需要根据键盘高度的变化来更新其 UI,这将阻止这种尝试。例如,假设键盘在密码文本字段中打开,并且手机在 SMS 中收到 OTP 代码,快速输入栏将显示在键盘上以自动填充,改变其高度。在这种情况下,@paulvs 的回答非常有效。 @badhanganesh 是的,我不再使用这种方法。我使用了一种更复杂的方法,在这里可以看到:github.com/mattneub/Programming-iOS-Book-Examples/blob/master/…【参考方案4】:最好的方法是添加通知并在您的目的解决后将其删除。
像这样。
- (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;
这应该可以解决您的问题。
【讨论】:
对不起,但这并不能解决问题,当我们离开视图时,我们会删除通知,这是与上述问题无关的标准做法,它与附件视图有关。 【参考方案5】:对于那些没有使用 inputAccessoryView 但仍然有问题的人,可能是由于使用了敏感(密码)字段。请参阅此 Stack Overflow 帖子并回答:keyboardWillShow in IOS8 with UIKeyboardWillShowNotification
【讨论】:
【参考方案6】:我一直在努力解决这个问题,经过半天的搜索和试验,我认为这是最短和最可靠的代码。它混合了许多答案,其中大部分我都忘记了我在哪里找到的(这里提到了其中的一部分)。
我的问题是 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];
动画到选项的转换似乎有效(我只能找到它正在使用的示例,而不是如何/为什么),但是,我不相信它会保持这种状态,因此使用“股票”可能是明智的选项。看来键盘使用了一些未指定的动画。
【讨论】:
以上是关于为啥每次选择另一个 TextField 时都会调用 KeyboardWillShowNotification?的主要内容,如果未能解决你的问题,请参考以下文章