UIKeyboardDidShowNotification called multiple time

2019-03-18 06:19发布

I am trying to move a UITextView above the keyboard whenever the keyboard appears/changes. Let's say I have the English keyboard displaying and then switch directly to the Chinese keyboard (which is taller than the standard English keyboard). In this scenario my text view always appears too high (to the naked eye, it looks like the text view is incorrectly offset by the size of the Chinese keyboard "input accessory view". I'd post an image but lack the reputation to do so :) ). I am adjusting my text view position when my app receives a UIKeyboardDidShowNotification (using UIKeyboardFrameEndUserInfoKey to get the height), and after some investigation UIKeyboardDidShowNotification is called multiple times, oftentimes with the incorrect keyboard dimensions (I've NSLogged the userInfo dictionary). I register for my keyboard notifications in ViewWillAppear and unregister in ViewWillDisappear. I am unable to determine what might be causing this notification to fire multiple times; my understanding is that this notification should only be fired one time, right after the keyboard finishes displaying. An additional note: I've NSLogged 'self' in the method that responds to UIKeyboardDidShowNotification and it is in fact always the same View Controller Object.

Even with this notification firing multiple times, however, I still do not understand why the keyboard height would be different for some of these notifications. One of the notifications always has the correct height, but when it is not the last notification fired the text view ends up in the wrong spot. Any insight on how to further troubleshoot would be much appreciated!

EDIT: The more I test the more it seems to be a problem with the Chinese keyboard specifically. Whenever I switch the keyboard from english to chinese I get three UIKeyboardWillShowNotifications:

2014-12-24 22:49:29.385 Example[1055:421943] info dictionary: {
UIKeyboardAnimationCurveUserInfoKey = 0;
UIKeyboardAnimationDurationUserInfoKey = 0;
UIKeyboardBoundsUserInfoKey = "NSRect: {{0, 0}, {320, 252}}";
UIKeyboardCenterBeginUserInfoKey = "NSPoint: {160, 460}";
UIKeyboardCenterEndUserInfoKey = "NSPoint: {160, 442}";
UIKeyboardFrameBeginUserInfoKey = "NSRect: {{0, 352}, {320, 216}}";
UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 316}, {320, 252}}";
}
2014-12-24 22:49:29.408 Example[1055:421943] info dictionary: {
UIKeyboardAnimationCurveUserInfoKey = 0;
UIKeyboardAnimationDurationUserInfoKey = 0;
UIKeyboardBoundsUserInfoKey = "NSRect: {{0, 0}, {320, 216}}";
UIKeyboardCenterBeginUserInfoKey = "NSPoint: {160, 442}";
UIKeyboardCenterEndUserInfoKey = "NSPoint: {160, 460}";
UIKeyboardFrameBeginUserInfoKey = "NSRect: {{0, 316}, {320, 252}}";
UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 352}, {320, 216}}";
}
2014-12-24 22:49:29.420 Example[1055:421943] info dictionary: {
UIKeyboardAnimationCurveUserInfoKey = 0;
UIKeyboardAnimationDurationUserInfoKey = 0;
UIKeyboardBoundsUserInfoKey = "NSRect: {{0, 0}, {320, 288}}";
UIKeyboardCenterBeginUserInfoKey = "NSPoint: {160, 442}";
UIKeyboardCenterEndUserInfoKey = "NSPoint: {160, 424}";
UIKeyboardFrameBeginUserInfoKey = "NSRect: {{0, 316}, {320, 252}}";
UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 280}, {320, 288}}";
}

The first one has the correct end height: 252. However, the next two are incorrect at 216 and 288. This happens reliably.

Here are a couple of snippets to demonstrate how I am managing subscriptions to notifications:

-(void)viewWillAppear:(BOOL)animated {

       [super viewWillAppear:animated];


       [self registerForKeyboardNotifications];


}

-(void)viewWillDisappear:(BOOL)animated {
       [super viewWillDisappear:animated];

       [[NSNotificationCenter defaultCenter] removeObserver:self
                                                name:UIKeyboardWillHideNotification
                                                object:nil];
       [[NSNotificationCenter defaultCenter] removeObserver:self
                                                name:UIKeyboardDidShowNotification
                                                object:nil];

}

- (void)registerForKeyboardNotifications {



      [[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(keyboardDidHide:)
                                             name:UIKeyboardWillHideNotification object:nil];

      [[NSNotificationCenter defaultCenter] addObserver:self      
                                         selector:@selector(keyboardDidShow:)                                       
                                             name:UIKeyboardDidShowNotification object:nil];
}

4条回答
兄弟一词,经得起流年.
2楼-- · 2019-03-18 06:44
- (void)keyboardDidAppear:(NSNotification *)note {
    CGSize keyboardSize = [note.userInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
    UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardSize.height, 0.0);
    textView.contentInset = contentInsets;
    textView.scrollIndicatorInsets = contentInsets;
}

- (void)keyboardWillBeHidden:(NSNotification *)note {
    UIEdgeInsets contentInsets = UIEdgeInsetsZero;
    textView.contentInset = contentInsets;
    textView.scrollIndicatorInsets = contentInsets;
}

The solution is from Apple's official solution here like 曹 said.

查看更多
▲ chillily
3楼-- · 2019-03-18 06:48

In my case, I register for my keyboard notifications in ViewWillAppear and unregister in ViewWillDisappear too. But, It will cause the UIKeyboardDidShowNotification handler be fired multiple times. Seem like the unregister method do not work, so, every time the view appears, the counts of Observer for UIKeyboardDidShowNotification plus 1. Then, you touch inside UITextField, multiple Observer be notified, handler be fired again and again.

So, you must register for keyboard notifications in ViewDidLoad and don't unregister. Just like the Apple mentioned in this page,

// Call this method somewhere in your view controller setup code.

I think the 'setup' means ViewDidLoad.And In the Handling the keyboard notifications code list,no ViewWillDisappear.

Here's my handler for keyboard notify, and it work.

   func keyboardWillShow(notification: NSNotification) {
    let userInfo = notification.userInfo!
    let keyboardHeight = (userInfo[UIKeyboardFrameEndUserInfoKey] as! NSValue).CGRectValue().height
    debugPrint("Before",NotifyView.frame)
    NotifyView.frame.offsetInPlace(dx: 0, dy: -keyboardHeight)
    debugPrint("After",NotifyView.frame)
}
func keyboardWillHide(notification: NSNotification) {//Do Nothing
}
查看更多
我欲成王,谁敢阻挡
4楼-- · 2019-03-18 07:05

If you're using the Cmd+K shortcut on the simulator to show/hide the keyboard, it may get called multiple times since that doesn't resign the textfield as the first responder.

If you instead use the keyboard's Return button then it will do the right thing and resign it.

查看更多
放荡不羁爱自由
5楼-- · 2019-03-18 07:08

The primary reason is that the your notification is invoked multiple times. For example, in swift, if you had the NSNotificationCenter.defaultCenter().addObserver(xx"keyboardWillShow"xx) inside viewWillAppear method and if you are going back and forth between the view then it will result in having multiple observer of the same "keyboardWillShow". Instead you should consider moving the addObserver call to "viewDidLoad()" method. Alternatively you can register/deregister observer when the view appears/disappears.

查看更多
登录 后发表回答