Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is UIKeyboardWillShowNotification called every time another TextField is selected?

I have a project that contains a UIScrollView and many UITextField inside it.

For the first time I select a UITextField, UIKeyboardWillShowNotification is called, which is fine. But whenever I select new UITextField (THE KEYBOARD IS STILL THERE), UIKeyboardWillShowNotification is called again !!!, which is weird.

I also set a symbolic breakpoint for [UIResponder resignFirstResponder] and I see that it is hit before and after UIKeyboardWillShowNotification is called !!!

The other thing is that UIKeyboardWillHideNotification is only called when I hit the "Done" button on the keyboard

I'm sure to not call any resignFirstResponder, becomeFirstResponder, endEditing anywhere. (I mean not call wrongly)

What can cause this problem ?

Here is the stacktrace enter image description here

like image 346
onmyway133 Avatar asked Mar 21 '14 04:03

onmyway133


4 Answers

To workaround the problem, I used the following code to cancel the UIKeyboardWillShowNotification callback if the keyboard's frame is not changing.

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
    }

    ...
}

For 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
    ...
}
like image 96
paulvs Avatar answered Oct 08 '22 13:10

paulvs


The problem is I set inputAccessoryView for the UITextField, and this cause UIKeyboardWillShowNotification being called again when new UITextField is selected

This article Working With Keyboard on iOS explains this well

Additional changes take place when we connect an external keyboard to the iPad. In this particular case, the notification behavior depends on the inputAccessoryView property of the control which was the reason for displaying the keyboard.

If inputAccessoryView is not present or its height is equal to 0 points, no keyboard notifications are sent. My guess is that this is because in this case, no visual changes take place in application. Otherwise, all notifications behave as expected – which means they are being sent as in the majority of cases when the keyboard is displayed or hidden in a normal (not undocked or split) state.

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

The same applies for when the TextField loses its first responder status

Note that using the same View for inputAccessoryView will cause UIKeyboardWillShowNotification only called once

like image 35
onmyway133 Avatar answered Oct 08 '22 11:10

onmyway133


In general I find that many things can cause spurious UIKeyboardWillShow and UIKeyboardWillHide notifications. My solution is to use a property to track whether the keyboard is already showing:

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
}

Those guards block exactly the spurious notifications, and all is well after that. And the keyboardShowing property can be useful for other reasons, so that it is something worth tracking anyway.

like image 5
matt Avatar answered Oct 08 '22 11:10

matt


The best approach is to Add notification & remove it once your purpose is solve.

like this .

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

Now write your code for movement of views & textField in keyboardWillShow & revert them back to position in keyboardWillHide methods.

Also remove the observers

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

You can also resign the responder when you press return key.

-(BOOL)textFieldShouldReturn:(UITextField *)textField {

    [_txtFieldEmail resignFirstResponder];
    [_txtFieldPassword resignFirstResponder];
    return YES;
}

That should solve your issue.

like image 1
Balram Tiwari Avatar answered Oct 08 '22 11:10

Balram Tiwari