Keyboard Events Handling using Protocols in Swift
In my previous post, I suggested a way to make MVC great again using Generics, extensions, and Protocols. Today we will build on top of that to handle keyboard events :)
The problem
Update UIView layout and adapt to system keyboard event notifications.
The Solution
We will introduce two new protocols and an extension.
The KeyboardObserving
Protocol
Conform to this protocol in a view controller to register to, unregister from keyboard events and pass notifications to its view
swift
protocol KeyboardObserving: AnyObject
func keyboardWillShow(_ notification: Notification)
func keyboardDidShow(_ notification: Notification)
func keyboardWillHide(_ notification: Notification)
func keyboardDidHide(_ notification: Notification)
func keyboardWillChangeFrame(_ notification: Notification)
func keyboardDidChangeFrame(_ notification: Notification)
func registerForKeyboardEvents()
func unregisterFromKeyboardEvents()
}
We will extend the KeyboardObserving
protocol with a default implementation, this will make its methods optional as well
The KeyboardControllable
Protocol
Conform to this protocol in a view to respond to keyboard events
swift
protocol KeyboardControllable: AnyObject {
func handleKeyboardWillShow(_ notification: Notification)
func handleKeyboardDidShow(_ notification: Notification)
func handleKeyboardWillHide(_ notification: Notification)
func handleKeyboardDidHide(_ notification: Notification)
func handleKeyboardWillChangeFrame(_ notification: Notification)
func handleKeyboardDidChangeFrame(_ notification: Notification)
}
Again we will extend the KeyboardControllable protocol with a default empty implementation, this will make its methods optional as well
Notification
Extensions
keyboardSize
and keyboardAnimationDuration
properties will give us an easy way to get keyboard info from the system notification
swift
extension Notification {
var keyboardSize: CGSize? {
return (userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.size
}
var keyboardAnimationDuration: Double? {
return userInfo?[UIKeyboardAnimationDurationUserInfoKey] as? Double
}
}
Example
Let’s update the same example from my previous tutorial to push up text fields in LoginView
when the keyboard is presented and center them back when the keyboard is hidden.
LoginView.swift
swift
// 1. conform to KeyboardControllable protocol
class LoginView: View, KeyboardControllable {
...
// 2. handle keyboard will show
func handleKeyboardWillShow(_ notification: Notification) {
// 3. get keyboard height from received notification
let keyboardSize = notification.keyboardSize
let keyboardHeight = keyboardSize?.height ?? 250
// 4. update stackView constrains based on keyboard height
stackView.snp.updateConstraints {
$0.bottom.equalToSuperview().inset(40 + keyboardHeight)
}
layoutIfNeeded()
}
// 5. handle keyboard will hide
func handleKeyboardWillHide(_ notification: Notification) {
stackView.snp.updateConstraints {
$0.bottom.equalToSuperview().inset(40)
}
layoutIfNeeded()
}
...
}
LoginViewController.swift
swift
// 1. conform to KeyboardObserving protocol
class LoginViewController: ViewController<LoginView>, KeyboardObserving {
override func viewDidLoad() {
...
// 2. register for keyboard event notifications
registerForKeyboardEvents()
}
// 3. observe keyboardWillShow
func keyboardWillShow(_ notification: Notification) {
customView.handleKeyboardWillShow(notification)
}
// 4. observe keyboardWillHide
func keyboardWillHide(_ notification: Notification) {
customView.handleKeyboardWillHide(notification)
}
...
deinit {
// 5. unregister from keyboard notifications
// to avoid memory leaks
unregisterFromKeyboardEvents()
}
}