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

时间:2014-03-21 04:03:50

标签: ios xcode notifications first-responder

我的项目中包含UIScrollView和许多UITextField

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

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

另一件事是UIKeyboardWillHideNotification仅在我按下键盘上的“完成”按钮时被调用

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

什么可能导致这个问题?

这是堆栈跟踪 enter image description here

6 个答案:

答案 0 :(得分:11)

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

这篇文章Working With Keyboard on iOS很好地解释了

  

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

     

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

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

UIKeyboardWillChangeFrameNotification
UIKeyboardWillShowNotification
UIKeyboardDidChangeFrameNotification
UIKeyboardDidShowNotification

同样适用于TextField失去其第一响应者状态

请注意,使用inputAccessoryView相同视图会导致UIKeyboardWillShowNotification仅被称为一次

答案 1 :(得分:7)

要解决此问题,如果键盘的框架没有更改,我使用以下代码取消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
    ...
}

答案 2 :(得分:2)

一般来说,我发现许多内容都会导致虚假的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属性可能因其他原因而有用,因此无论如何它都值得跟踪。

答案 3 :(得分:1)

最好的方法是添加通知&一旦你的目的得到解决就把它删除。

像这样。

- (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;
}

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

答案 4 :(得分:1)

对于那些不使用inputAccessoryView但仍有问题的人,可能是由于使用敏感(密码)字段。请参阅此Stack Overflow帖子并回答:keyboardWillShow in IOS8 with UIKeyboardWillShowNotification

答案 5 :(得分:0)

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

我的问题是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];

}

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