Keyboard Events Handling using Protocols in Swift

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() } }

Where to go from here?

  • See the example Xcode project on Github
  • If you have any questions or suggestions, don’t hesitate to reach out to me on Twitter.