Skip to content

Instantly share code, notes, and snippets.

@roymckenzie
Last active September 6, 2022 19:42
Show Gist options
  • Save roymckenzie/35684bf3ae6df02f60a41490a9843008 to your computer and use it in GitHub Desktop.
Save roymckenzie/35684bf3ae6df02f60a41490a9843008 to your computer and use it in GitHub Desktop.
Easy way to get your view controllers to respect appearance of the keyboard.
// KeyboardAvoidable
// Roy McKenzie
protocol KeyboardAvoidable: class {
func addKeyboardObservers(customBlock: ((CGFloat) -> Void)?)
func removeKeyboardObservers()
var layoutConstraintsToAdjust: [NSLayoutConstraint] { get }
}
var KeyboardShowObserverObjectKey: UInt8 = 1
var KeyboardHideObserverObjectKey: UInt8 = 2
extension KeyboardAvoidable where Self: UIViewController {
var keyboardShowObserverObject: NSObjectProtocol? {
get {
return objc_getAssociatedObject(self,
&KeyboardShowObserverObjectKey) as? NSObjectProtocol
}
set {
objc_setAssociatedObject(self,
&KeyboardShowObserverObjectKey,
newValue,
.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
var keyboardHideObserverObject: NSObjectProtocol? {
get {
return objc_getAssociatedObject(self,
&KeyboardHideObserverObjectKey) as? NSObjectProtocol
}
set {
objc_setAssociatedObject(self,
&KeyboardHideObserverObjectKey,
newValue,
.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
func addKeyboardObservers(customBlock: ((CGFloat) -> Void)? = nil) {
keyboardShowObserverObject = NotificationCenter.default.addObserver(forName: .UIKeyboardWillShow,
object: nil,
queue: nil) { [weak self] notification in
guard let height = self?.getKeyboardHeightFrom(notification: notification) else { return }
if let customBlock = customBlock {
customBlock(height)
return
}
self?.layoutConstraintsToAdjust.forEach {
$0.constant = height
}
UIView.animate(withDuration: 0.2){
self?.view.layoutIfNeeded()
}
}
keyboardHideObserverObject = NotificationCenter.default.addObserver(forName: .UIKeyboardWillHide,
object: nil,
queue: nil) { [weak self] notification in
if let customBlock = customBlock {
customBlock(0)
return
}
self?.layoutConstraintsToAdjust.forEach {
$0.constant = 0
}
UIView.animate(withDuration: 0.2){
self?.view.layoutIfNeeded()
}
}
}
private func getKeyboardHeightFrom(notification: Notification) -> CGFloat {
guard let info = notification.userInfo else { return .leastNormalMagnitude }
guard let value = info[UIKeyboardFrameEndUserInfoKey] as? NSValue else { return .leastNormalMagnitude }
let keyboardSize = value.cgRectValue.size
return keyboardSize.height
}
func removeKeyboardObservers() {
if let keyboardShowObserverObject = keyboardShowObserverObject {
NotificationCenter.default.removeObserver(keyboardShowObserverObject)
}
if let keyboardHideObserverObject = keyboardHideObserverObject {
NotificationCenter.default.removeObserver(keyboardHideObserverObject)
}
keyboardShowObserverObject = nil
keyboardHideObserverObject = nil
}
}
// Example Implementation
// final class NiceViewController: UIViewController {
//
// @IBOutlet weak var scrollViewBottomConstraint: NSLayoutConstraint!
//
// override func viewWillAppear(animated: Bool) {
// super.viewWillAppear(animated)
//
// addKeyboardObservers()
// }
//
// override func viewWillDisappear(animated: Bool) {
// super.viewWillDisappear(animated)
//
// removeKeyboardObservers()
// }
// }
//
// extension NiceViewController: KeyboardAvoidable {
//
// var layoutConstraintsToAdjust: [NSLayoutConstraint] {
// return [scrollViewBottomConstraint]
// }
// }
@Drenov
Copy link

Drenov commented Dec 1, 2017

Don't removeKeyboardObservers() should be called on the beginning of addKeyboardObservers() to prevent issues when it was called more than once?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment