Keyboard Events Handling using Protocols in Swift

SHARE

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

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

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

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

// 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

// 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.
SoftwareSwiftUIKitiOS

You made it to the end. You're Awesome!

Here is something more to read

Protocol Oriented Extensions

Use the power of protocols and generic types to avoid extension conflicts

Making MVC Great Again!

Use generics, protocols, and extensions to get rid of massive view controllers

This is a fully integrated open-source project that uses NextJS, Redux, and Django to build. Grab your copy from Github

Copyright © 2019 Omar Albeik. All rights reserved.