visit
Manually handle the dismissal of the keyboard
Moving text-fields on your screen when the Keyboard appears & disappears
This is the quickest way to implement keyboard dismissal. Just set a Tap gesture on the main View and hook that gesture with a function which calls
view.endEditing
. Apple docs say the following about
endEditing
- Causes the view (or one of its embedded text fields) to resign the first responder status.
class ChatLoginVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//We make a call to our keyboard handling function as soon as the view is loaded.
initializeHideKeyboard()
}
}
extension ChatVC {
func initializeHideKeyboard(){
//Declare a Tap Gesture Recognizer which will trigger our dismissMyKeyboard() function
let tap: UITapGestureRecognizer = UITapGestureRecognizer(
target: self,
action: #selector(dismissMyKeyboard))
//Add this tap gesture recognizer to the parent view
view.addGestureRecognizer(tap)
}
@objc func dismissMyKeyboard(){
//endEditing causes the view (or one of its embedded text fields) to resign the first responder status.
//In short- Dismiss the active keyboard.
view.endEditing(true)
}
}
Another great option is to use that non-functional keyboard Return/Done/Continue key. It’s just sitting there doing nothing unless we have specified some custom behavior in
textFieldShouldReturn
function. textFieldShouldReturn
asks the delegate if the text field should process the pressing of the return button. textFieldShouldReturn
fires when the user presses the Return key on the keyboard. Hence, here we check - Is there any other text-field in the view whose tag is +1 greater than the current text-field on which the return key was pressed. If yes → then move the cursor to that next text-field. If No → Dismiss the keyboard.
class ChatLoginVC: UIViewController {
@IBOutlet weak var emailTF: UITextField!
@IBOutlet weak var passwordTF: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
emailTF.delegate = self
passwordTF.delegate = self
emailTF.tag = 1
passwordTF.tag = 2
}
}
extension ChatLoginVC: UITextFieldDelegate {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
//Check if there is any other text-field in the view whose tag is +1 greater than the current text-field on which the return key was pressed. If yes → then move the cursor to that next text-field. If No → Dismiss the keyboard
if let nextField = self.view.viewWithTag(textField.tag + 1) as? UITextField {
nextField.becomeFirstResponder()
} else {
textField.resignFirstResponder()
}
return false
}
}
The above solution with
textFieldShouldReturn
works great but unfortunately, there is no “Return” key on iOS Number Pad. To overcome our barrier here in the case of Number Pad, we can add a simple Toolbar above our Keyboard with a “Done” button. This “Done” button is going to call the same function we used in our Tap Gesture Method above and would dismiss our keyboard. To keep consistency across the fields, you can use Toolbars across the app as a common patter to dismiss your keyboard.
class ChatLoginVC: UIViewController {
@IBOutlet weak var pinTF: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
setupToolbar()
}
func setupToolbar(){
//Create a toolbar
let bar = UIToolbar()
//Create a done button with an action to trigger our function to dismiss the keyboard
let doneBtn = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(dismissMyKeyboard))
//Create a felxible space item so that we can add it around in toolbar to position our done button
let flexSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
//Add the created button items in the toobar
bar.items = [flexSpace, flexSpace, doneBtn]
bar.sizeToFit()
//Add the toolbar to our textfield
pinTF.inputAccessoryView = bar
}
@objc func dismissMyKeyboard(){
view.endEditing(true)
}
}
class ChatVC: UIViewController {
@IBOutlet weak var messageTF: UITextField!
@IBOutlet weak var backgroundSV: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
//Subscribe to a Notification which will fire before the keyboard will show
subscribeToNotification(UIResponder.keyboardWillShowNotification, selector: #selector(keyboardWillShowOrHide))
//Subscribe to a Notification which will fire before the keyboard will hide
subscribeToNotification(UIResponder.keyboardWillHideNotification, selector: #selector(keyboardWillShowOrHide))
//We make a call to our keyboard handling function as soon as the view is loaded.
initializeHideKeyboard()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
//Unsubscribe from all our notifications
unsubscribeFromAllNotifications()
}
}
extension ChatVC {
func initializeHideKeyboard(){
//Declare a Tap Gesture Recognizer which will trigger our dismissMyKeyboard() function
let tap: UITapGestureRecognizer = UITapGestureRecognizer(
target: self,
action: #selector(dismissMyKeyboard))
//Add this tap gesture recognizer to the parent view
view.addGestureRecognizer(tap)
}
@objc func dismissMyKeyboard(){
//endEditing causes the view (or one of its embedded text fields) to resign the first responder status.
//In short- Dismiss the active keyboard.
view.endEditing(true)
}
}
extension ChatVC {
func subscribeToNotification(_ notification: NSNotification.Name, selector: Selector) {
NotificationCenter.default.addObserver(self, selector: selector, name: notification, object: nil)
}
func unsubscribeFromAllNotifications() {
NotificationCenter.default.removeObserver(self)
}
@objc func keyboardWillShowOrHide(notification: NSNotification) {
// Get required info out of the notification
if let scrollView = backgroundSV, let userInfo = notification.userInfo, let endValue = userInfo[UIResponder.keyboardFrameEndUserInfoKey], let durationValue = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey], let curveValue = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] {
// Transform the keyboard's frame into our view's coordinate system
let endRect = view.convert((endValue as AnyObject).cgRectValue, from: view.window)
// Find out how much the keyboard overlaps our scroll view
let keyboardOverlap = scrollView.frame.maxY - endRect.origin.y
// Set the scroll view's content inset & scroll indicator to avoid the keyboard
scrollView.contentInset.bottom = keyboardOverlap
scrollView.scrollIndicatorInsets.bottom = keyboardOverlap
let duration = (durationValue as AnyObject).doubleValue
let options = UIView.AnimationOptions(rawValue: UInt((curveValue as AnyObject).integerValue << 16))
UIView.animate(withDuration: duration!, delay: 0, options: options, animations: {
self.view.layoutIfNeeded()
}, completion: nil)
}
}
}
class ChatVC: KeyboardHandlingBaseVC {
override func viewDidLoad() {
super.viewDidLoad()
}
}
class KeyboardHandlingBaseVC: UIViewController {
@IBOutlet weak var backgroundSV: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
subscribeToNotification(UIResponder.keyboardWillShowNotification, selector: #selector(keyboardWillShowOrHide))
subscribeToNotification(UIResponder.keyboardWillHideNotification, selector: #selector(keyboardWillShowOrHide))
initializeHideKeyboard()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
unsubscribeFromAllNotifications()
}
}
// MARK: Keyboard Dismissal Handling on Tap
private extension KeyboardHandlingBaseVC {
func initializeHideKeyboard(){
let tap: UITapGestureRecognizer = UITapGestureRecognizer(
target: self,
action: #selector(dismissMyKeyboard))
view.addGestureRecognizer(tap)
}
@objc func dismissMyKeyboard(){
view.endEditing(true)
}
}
// MARK: Textfield Visibility Handling with Scroll
private extension KeyboardHandlingBaseVC {
func subscribeToNotification(_ notification: NSNotification.Name, selector: Selector) {
NotificationCenter.default.addObserver(self, selector: selector, name: notification, object: nil)
}
func unsubscribeFromAllNotifications() {
NotificationCenter.default.removeObserver(self)
}
@objc func keyboardWillShowOrHide(notification: NSNotification) {
if let scrollView = backgroundSV, let userInfo = notification.userInfo, let endValue = userInfo[UIResponder.keyboardFrameEndUserInfoKey], let durationValue = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey], let curveValue = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] {
let endRect = view.convert((endValue as AnyObject).cgRectValue, from: view.window)
let keyboardOverlap = scrollView.frame.maxY - endRect.origin.y
scrollView.contentInset.bottom = keyboardOverlap
scrollView.scrollIndicatorInsets.bottom = keyboardOverlap
let duration = (durationValue as AnyObject).doubleValue
let options = UIView.AnimationOptions(rawValue: UInt((curveValue as AnyObject).integerValue << 16))
UIView.animate(withDuration: duration!, delay: 0, options: options, animations: {
self.view.layoutIfNeeded()
}, completion: nil)
}
}
}
This article was originally published at .
If you enjoyed reading this article and learned something, share it amongst your developer friends/colleagues and spread the knowledge! If you have anything to discuss regarding Keyboard Handling or any other topic related to Mobile Apps, Development, Designing, Engineering… Drop a comment below or DM me on :)
If you find this interesting, chances are you’ll find my twitter feed too! I keep sharing articles and stuff related to development, design and productivity on a daily basis. So hop on, follow me on and enjoy the learning ride!
Till the next Article…