UIKeyboardWillShowNotification is called three times

I need to move a UIView up as soon as the keyboard will become visible. But the problem I'm facing right now is that my UIKeyboardWillShowNotification is called three times when I'm using a custom Keyboard (e.g. SwiftKey) which results in a bad animation. Is there a way to handle only the last notification? I could easily dodge the first one because the height is 0, but the second one looks like a valid height and I don't find an answer on how to solve this. Here is what I've so far:

override func viewDidLoad() {
    super.viewDidLoad()

    NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillAppear:", name: UIKeyboardWillShowNotification, object: nil)
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillDisappear:", name: UIKeyboardWillHideNotification, object: nil)
}

override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(animated)

    NSNotificationCenter.defaultCenter().removeObserver(self)
}

func keyboardWillAppear(notification: NSNotification){
    print("keyboard appear")
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.CGRectValue() {
        print("with height: \(keyboardSize.height)")
        if keyboardSize.height == 0.0 {
            return
        }
        self.txtViewBottomSpace.constant = keyboardSize.height
        UIView.animateWithDuration(0.4, animations: { () -> Void in
            self.view.layoutIfNeeded()
        })
    }
}

func keyboardWillDisappear(notification: NSNotification){
    print("Keyboard disappear")
    self.txtViewBottomSpace.constant = 0.0
    UIView.animateWithDuration(0.4, animations: { () -> Void in
        self.view.layoutIfNeeded()
    })
}

My Log output is:

keyboard appear with height: 0.0 keyboard appear with height: 216.0 keyboard appear with height: 258.0 Keyboard disappear

So is there any way to only handle the third notification and "ignore" the first two?

Answers


Set all bellow fields to NO can resolve this problem.

Capitalizaion: None
Correction: No
Smart Dashes: No
Smart insert: No
Smart Quote: No
Spell Checking: No

Change the notification name UIKeyboardDidShowNotification and UIKeyboardDidHideNotification then solve the problem

 override func viewDidLoad() {
    super.viewDidLoad()

    NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillAppear:", name: UIKeyboardDidShowNotification, object: nil)
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillDisappear:", name: UIKeyboardDidHideNotification, object: nil)
}

override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(animated)

    NSNotificationCenter.defaultCenter().removeObserver(self)
}

func keyboardWillAppear(notification: NSNotification){
    print("keyboard appear")
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.CGRectValue() {
        print("with height: \(keyboardSize.height)")
        if keyboardSize.height == 0.0 {
            return
        }
        self.txtViewBottomSpace.constant = keyboardSize.height
        UIView.animateWithDuration(0.4, animations: { () -> Void in
            self.view.layoutIfNeeded()
        })
    }
}

func keyboardWillDisappear(notification: NSNotification){
    print("Keyboard disappear")
    self.txtViewBottomSpace.constant = 0.0
    UIView.animateWithDuration(0.4, animations: { () -> Void in
        self.view.layoutIfNeeded()
    })
}

I suggest to replace the static animation duration (0.4) with the animation duration of the keyboard, returned in the userInfo dictionary of the notification under UIKeyboardAnimationDurationUserInfoKey. In this way your animation will be in sync with the keyboard animation. You can also retrieve the animation curve used by the keyboard with the UIKeyboardAnimationCurveUserInfoKey key.

func keyboardWillAppear(notification: NSNotification){
    print("keyboard appear")
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.CGRectValue() {
        let animationDuration = notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey]?.doubleValue;
        print("with height: \(keyboardSize.height)")
        if keyboardSize.height == 0.0 {
            return
        }
        self.txtViewBottomSpace.constant = keyboardSize.height
        UIView.animateWithDuration(animationDuration!, delay: 0.0, options: .BeginFromCurrentState, animations: { () -> Void in
            self.view.layoutIfNeeded()
        })
    }
}

func keyboardWillDisappear(notification: NSNotification){
    print("Keyboard disappear")
    let animationDuration = notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey]?.doubleValue;
    self.txtViewBottomSpace.constant = 0.0
        UIView.animateWithDuration(animationDuration!, delay: 0.0, options: .BeginFromCurrentState, animations: { () -> Void in
        self.view.layoutIfNeeded()
    })
}

The reason for this is because keyboards can have different sizes, especially third party ones. So the first notification you receive will always be for the default system height, and any you receive after that will include the new heights of a third party keyboard extension if one is loaded. In order to get around this, in your method that handles the notification, you need to get the height originally, and then set that as a default height (I think 226). Then set a variable to this first height, and then for resulting calls to the notification method you can check if the new height is greater than the original height, and if it is you can find the delta, and then readjust your frames accordingly.


Need Your Help

Python User Input File Name

python if-statement python-3.x filenames user-input

I'm trying to write a program that takes a users input for a file name, and then either opens the file to add to it, or prints what is already in the file, so far this is what I have: (I keep getti...

ClearCase UCM: Is it possible to delete a project?

clearcase clearcase-ucm

Can a ClearCase administrator or a project manager delete a project including all its streams, views, baselines, activities etc.? How?