Skip to content

Instantly share code, notes, and snippets.

@zackdotcomputer
Last active September 7, 2020 21:46
Show Gist options
  • Save zackdotcomputer/66cc54a50c3cd763022067de5e088466 to your computer and use it in GitHub Desktop.
Save zackdotcomputer/66cc54a50c3cd763022067de5e088466 to your computer and use it in GitHub Desktop.
Make UIViewControllers automatically aware of the keyboard
//
// UIViewController+KeyboardAware.swift
//
// Created by Zack Sheppard.
// Copyright © 2020 Zack Sheppard. All rights reserved.
// Available under the MIT License
//
import UIKit
/// Conforming to this protocol and calling startAvoidingKeyboard will cause
/// your view to automatically shrink to avoid the keyboard when it comes
/// on screen.
@objc protocol KeyboardAware {
/// Implement this function for additional animation responses to the keyboard
func customAnimationForNewKeyboardFrame(_ frame: CGRect)
}
// System static variable for the keyboard - is true if it is currently moving
fileprivate var isKeyboardMoving: Bool = false
/// This extension gives UIViewControllers automatic KeyboardAware ability, if you call startAvoidingKeyboard.
/// Based on idea from https://stackoverflow.com/questions/45399178/
extension UIViewController: KeyboardAware {
var keyboardIsAnimating: Bool {
return isKeyboardMoving
}
/// This view controller should start resizing itself to avoid the keyboard
func startAvoidingKeyboard() {
NotificationCenter.default.addObserver(
self,
selector: #selector(handleKeyboardFrameWillChangeNotification(_:)),
name: UIResponder.keyboardWillChangeFrameNotification,
object: nil
)
}
/// This view controller should stop resizing itself to avoid the keyboard
func stopAvoidingKeyboard() {
NotificationCenter.default.removeObserver(
self,
name: UIResponder.keyboardWillChangeFrameNotification,
object: nil
)
self.additionalSafeAreaInsets.bottom = 0
}
func customAnimationForNewKeyboardFrame(_ frame: CGRect) {
// Feel free to override this to add custom handling
}
@objc private func handleKeyboardFrameWillChangeNotification(_ notification: Notification) {
guard let userInfo = notification.userInfo,
let keyboardFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else {
isKeyboardMoving = false
return
}
let keyboardFrameInView = view.convert(keyboardFrame, from: nil)
let safeAreaFrame = view.safeAreaLayoutGuide.layoutFrame.insetBy(dx: 0, dy: -additionalSafeAreaInsets.bottom)
let intersection = safeAreaFrame.intersection(keyboardFrameInView)
let animationDuration: TimeInterval = (notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
let animationCurve: UIView.AnimationOptions
if let animationCurveRawValue = (notification.userInfo?[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber)?.uintValue {
animationCurve = UIView.AnimationOptions(rawValue: animationCurveRawValue)
} else {
animationCurve = UIView.AnimationOptions.curveEaseInOut
}
isKeyboardMoving = true
UIView.animate(withDuration: animationDuration, delay: 0, options: animationCurve, animations: {
self.additionalSafeAreaInsets.bottom = intersection.height
self.customAnimationForNewKeyboardFrame(intersection)
self.view.layoutIfNeeded()
}, completion: { (_) in
isKeyboardMoving = false
})
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment