The secret is to listen to the UIKeyboardWillChangeFrame
notification that is triggered whenever the keyboard is shown/hidden from your app or from another app running side by side with yours.
I created this extension to make it easy to start/stop observing those events (I call them in viewWillAppear
/Disappear
), and easily get the obscuredHeight
that is usually used to adjust the bottom contentInset
of your table/collection/scrollview.
@objc protocol KeyboardObserver
{
func startObservingKeyboard() // Call this in your controller's viewWillAppear
func stopObservingKeyboard() // Call this in your controller's viewWillDisappear
func keyboardObscuredHeight() -> CGFloat
@objc optional func adjustLayoutForKeyboardObscuredHeight(_ obscuredHeight: CGFloat, keyboardFrame: CGRect, keyboardWillAppearNotification: Notification) // Implement this in your controller and adjust your bottom inset accordingly
}
var _keyboardObscuredHeight:CGFloat = 0.0;
extension UIViewController: KeyboardObserver
{
func startObservingKeyboard()
{
NotificationCenter.default.addObserver(self, selector: #selector(observeKeyboardWillChangeFrameNotification(_:)), name: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil)
}
func stopObservingKeyboard()
{
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil)
}
func observeKeyboardWillChangeFrameNotification(_ notification: Notification)
{
guard let window = self.view.window else {
return
}
let animationID = "\(self) adjustLayoutForKeyboardObscuredHeight"
UIView.beginAnimations(animationID, context: nil)
UIView.setAnimationCurve(UIViewAnimationCurve(rawValue: (notification.userInfo![UIKeyboardAnimationCurveUserInfoKey]! as AnyObject).intValue)!)
UIView.setAnimationDuration((notification.userInfo![UIKeyboardAnimationCurveUserInfoKey]! as AnyObject).doubleValue)
let keyboardFrame = (notification.userInfo![UIKeyboardFrameEndUserInfoKey]! as AnyObject).cgRectValue
_keyboardObscuredHeight = window.convert(keyboardFrame!, from: nil).intersection(window.bounds).size.height
let observer = self as KeyboardObserver
observer.adjustLayoutForKeyboardObscuredHeight!(_keyboardObscuredHeight, keyboardFrame: keyboardFrame!, keyboardWillAppearNotification: notification)
UIView.commitAnimations()
}
func keyboardObscuredHeight() -> CGFloat
{
return _keyboardObscuredHeight
}
}