13

Considering the new QuickType section of the keyboard.

Is it quite true that one can use ONLY a notification for UIKeyboardWillChangeFrameNotification,

and simply "not bother with" the "older" UIKeyboardWillShowNotification and UIKeyboardWillHideNotification ?

Testing seems to show it works perfectly, using ONLY keyboardFrameDidChange - but we could be missing something?

BTW here's an example of how to use UIKeyboardWillChangeFrameNotification https://stackoverflow.com/a/26226732/294884

Community
  • 1
  • 1
Fattie
  • 27,874
  • 70
  • 431
  • 719

1 Answers1

36

Updated for Swift 5 on 2021-05-17

It is definitely possible and can cut your code just about in half. The following example uses Auto Layout for a lot of the heavy lifting.

NotificationCenter.default.addObserver(
    forName: UIResponder.keyboardWillChangeFrameNotification,
    object: nil,
    queue: nil
) { (notification) in
    guard let userInfo = notification.userInfo,
          let frameEnd = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect,
          let curveRaw = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? Int,
          let curve = UIView.AnimationCurve(rawValue: curveRaw),
          let duration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval
    else { return }

    // Convert the frame rects you're interested in to a common coordinate space
    let keyboardRect = self.view.convert(frameEnd, from: nil)
    let formContainerRect = self.view.convert(self.formContainerView.frame, from: nil)

    // Calculate their intersection and adjust your constraints accordingly
    let intersection = keyboardRect.intersection(formContainerRect)
    if !intersection.isNull {
        // Some overlap; adjust the bottom of your views (what you do here will vary)
        self.formContainerBottomConstraint?.constant = intersection.size.height
    } else {
        // No overlap; reset your views to its default position
        self.formContainerBottomConstraint?.constant = 0
    }

    let animator = UIViewPropertyAnimator.init(duration: duration, curve: curve) {
        self.view.layoutIfNeeded()
    }
    animator.startAnimation()
}

self.formContainerBottomConstraint is a NSLayoutConstraint that binds the bottom of my (imaginary) form to the bottom of my view. This code animates the field up when the keyboard appears and down when it disappears.

All of that was possible in iOS < 8 by using a combination of UIKeyboardWillShowNotification and UIKeyboardWillHideNotification. But! As you say, iOS 8 introduced the QuickType section which can be collapsed or expanded by the user. This solution is super versatile and will let your app respond to whatever keyboard changes the OS throws your way.

adamrothman
  • 956
  • 9
  • 9
  • Is there a way to control when it scrolls? On the iphone 6 I don't need the fields to scroll when the user is editing on the top most text field but when the edit the bottom text field it should scroll to make room. – Todd Hoff Feb 09 '15 at 18:34
  • Is the use of `UIViewAnimationOptions(UInt(curve))` correct? The documentation says that `curve` is a `UIViewAnimationCurve`, and looking at the definitions, this does not appear to line up with the values for `UIViewAnimationOptions`. Even better, the `curve` value returned in practice in this notification is `7` -- a private/undefined value, so a simple switch statement to convert between them won't cut it... – Dave Peck Sep 01 '15 at 19:31
  • 1
    Is there a solution in objective-c? – new2ios Sep 08 '15 at 14:31
  • @ToddHoff You would need to check which of your text fields is the current first responder inside the event handler: `self.bottomTextField.isFirstResponder`. – adamrothman Dec 01 '15 at 06:18
  • 1
    @DavePeck You're right, and based on my research there isn't really a great/Apple-recommended way to convert between the two. I'll update the answer with what I think is the best option as of right now (and to Swift 2). – adamrothman Dec 01 '15 at 06:23
  • I found myself needing to do this again for a new project and thought it would be a good time to update the answer for Swift 4. Happy hacking! – adamrothman May 25 '18 at 07:22