1

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?

Pascal_AC
  • 541
  • 4
  • 17

4 Answers4

1

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
Quy Pv
  • 21
  • 5
0

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()
    })
}
LorenzOliveto
  • 7,796
  • 1
  • 20
  • 47
  • Thank you for the suggestion about the duration. But the problem stays the same, when the keyboard appears everything "jumps" to their new location but there is no smooth animation because the method is called multiple times :( – Pascal_AC Mar 16 '16 at 10:57
  • Try to add the .BeginFromCurrentState option to your animation. I edited my answer with the option added. – LorenzOliveto Mar 16 '16 at 11:06
  • In alternative you could check if your self.txtViewBottomSpace.constant is 0 to start the animation. – LorenzOliveto Mar 16 '16 at 11:09
  • The `.BeginFromCurrentState` doesn't change anything :( – Pascal_AC Mar 16 '16 at 11:11
  • But the `self.txtViewBottomSpace.constant` is probably not 0 anymore when the correct notification will be fired because there is one "incorrect" notification before, or am I wrong? – Pascal_AC Mar 16 '16 at 11:13
  • Yes, you are right. Unfortunately there aren't any options that come to my mind right now. – LorenzOliveto Mar 16 '16 at 11:15
  • I did read something about checking if there is another notification in the run loop but I've no idea how to do this :/ – Pascal_AC Mar 16 '16 at 11:20
  • I think that this is a particular problem of the SwiftKey keyboard. Its appearance animation is not smooth at all. I cannot think of a general solution (addressing all the keyboard) for that problem. – LorenzOliveto Mar 16 '16 at 11:31
  • I tested it with all custom keyboard I own (Fleksy, SwiftKey and Minuum) and I've the same problem with every keyboard so I think it's a custom keyboard and not a SwiftKey problem. I think it'd solve the problem if I could check if the current notification is the last one but I don't know if that's possible. – Pascal_AC Mar 16 '16 at 11:40
0

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()
    })
}
Muzahid
  • 5,072
  • 2
  • 24
  • 42
  • Thanks for your suggestion but when I change the notification to DidAppear it's called when the Keyboard is already visible so I can't make an animation of the View moving upwards with the keyboard. – Pascal_AC Mar 16 '16 at 10:59
0

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.

pbush25
  • 5,228
  • 2
  • 26
  • 35