January 27, 2015
It’s become pretty standard for the iOS keyboard to be dismissed when you tap anywhere off of the keyboard. Unfortunately, this behaviour isn’t standard so you get to implement it yourself.
This tutorial has been updated to Swift 2.0.
Start with a view controller with a textfield and gesture recognizer to recognize when they tap off of the keyboard. (Yes, hard-coding sizes is bad but it’s the quickest way to show an example without half a dozen screenshots of setting stuff up in IB or a bunch of cryptic auto-layout constraints):
import UIKit
class ViewController: UIViewController {
var keyboardDismissTapGesture: UIGestureRecognizer?
var textfield: UITextField?
override func viewDidLoad() {
super.viewDidLoad()
textfield = UITextField(frame: CGRect(x: 20, y: 20,
width: self.view.frame.size.width - 40,
height: 40))
textfield!.borderStyle = UITextBorderStyle.Line
self.view.addSubview(textfield!)
}
}
We need to hook up the gesture recognizer so that, when the keyboard is being shown, tapping on the main view will hide the keyboard. We can dismiss the keyboard using resignFirstResponder:
func dismissKeyboard(sender: AnyObject) {
textfield?.resignFirstResponder()
}
And we need to know when the keyboard is shown and hidden. Fortunately, iOS already has events you can subscribe to for that:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
NSNotificationCenter.defaultCenter().addObserver(self,
selector: Selector("keyboardWillShow:"),
name: UIKeyboardWillShowNotification,
object: nil)
NSNotificationCenter.defaultCenter().addObserver(self,
selector: Selector("keyboardWillHide:"),
name: UIKeyboardWillHideNotification,
object: nil)
}
So we’re telling the default NSNotificationCenter to let our class know when the keyboard is shown (UIKeyboardWillShowNotification) or when it’s hidden (UIKeyboardWillHideNotification). It’ll call our functions (that we haven’t implemented yet): keyboardWillShow: and keyboardWillHide:
keyboardWillShow: needs to create and add the gesture recognizer (if it’s not already there, which shouldn’t happen but let’s be careful anyway). The gesture recognizer is applied to the main view. It calls our dismissKeyboard: function above when the user taps once on the main view (i.e., the section above the keyboard).
func keyboardWillShow(notification: NSNotification) {
if keyboardDismissTapGesture == nil
{
keyboardDismissTapGesture = UITapGestureRecognizer(target: self,
action: Selector("dismissKeyboard:"))
self.view.addGestureRecognizer(keyboardDismissTapGesture!)
}
}
Now we probably don’t want that code getting called if the keyboard isn’t currently shown. It won’t actually crash but we’d be calling a bunch of code that isn’t necessary, so let’s set up keyboardWillHide to remove the gesture recognizer:
func keyboardWillHide(notification: NSNotification) {
if keyboardDismissTapGesture != nil
{
self.view.removeGestureRecognizer(keyboardDismissTapGesture!)
keyboardDismissTapGesture = nil
}
}
Now you can run the app and play with it to make sure it’s working.
It doesn’t matter in the example code since there’s only one view but we should remove the observer from the notification center when the view isn’t being shown anymore:
override func viewWillDisappear(animated: Bool) {
NSNotificationCenter.defaultCenter().removeObserver(self)
super.viewWillDisappear(animated)
}
So all together:
import UIKit
class ViewController: UIViewController {
var keyboardDismissTapGesture: UIGestureRecognizer?
var textfield: UITextField?
override func viewDidLoad() {
super.viewDidLoad()
textfield = UITextField(frame: CGRect(x: 20, y: 20, width: self.view.frame.size.width - 40, height: 40))
textfield!.borderStyle = UITextBorderStyle.Line
self.view.addSubview(textfield!)
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name: UIKeyboardWillHideNotification, object: nil)
}
override func viewWillDisappear(animated: Bool) {
NSNotificationCenter.defaultCenter().removeObserver(self)
super.viewWillDisappear(animated)
}
func keyboardWillShow(notification: NSNotification) {
if keyboardDismissTapGesture == nil
{
keyboardDismissTapGesture = UITapGestureRecognizer(target: self, action: Selector("dismissKeyboard:"))
self.view.addGestureRecognizer(keyboardDismissTapGesture!)
}
}
func keyboardWillHide(notification: NSNotification) {
if keyboardDismissTapGesture != nil
{
self.view.removeGestureRecognizer(keyboardDismissTapGesture!)
keyboardDismissTapGesture = nil
}
}
func dismissKeyboard(sender: AnyObject) {
textfield?.resignFirstResponder()
}
}
Or grab it from GitHub: UIKeyboardDismiss demo code
If you’re adding this to a more complex app you might find that you need to adjust which view you’re adding the gesture recognizer to. Often it’s a tableview or some other subview, not self.view.
You’ll also want to test out tapping all the different views to see if it behaves as you expect, e.g., does the keyboard get dismissed if you tap on the nav bar or various buttons.
Experiment with nice UI like adding a semi-transparent view over the main view to hide everything but the textfield you’re focused on that fades in and out when they keyboard is shown.