Keyboard Events Handling using Protocols in Swift

22/3/2018

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

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
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

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
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

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
// 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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
// 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.
  • iOS
  • Software
  • Swift
  • UIKit

Comments

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

Here is something more to read

17/2/2019

Protocol Oriented Extensions

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

8/3/2018

Making MVC Great Again!

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

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

Copyright © 2020 Omar Albeik. All rights reserved.