How to dismiss keyboard when touching anywhere outside UITextField (in swift)?

I'm working on a project that have a UIViewController, on the view controller there is a UIScrollView and a UITextField on the scrollview. like this: I'm trying to dismiss the keyboard and hide it after typing some text in the textfield and tap anywhere outside the textfield. I've tried the following code:

override func viewDidLoad() {
    super.viewDidLoad()
    self.textField.delegate = self;
}

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    self.view.endEditing(true)
}

It works for me when I tap outside the scrollview, but when I tap on the scrollview nothing happens and the keyboard doesn't hide.

Is there any way to dismiss the keyboard when tapping anywhere outside the textfield? thanks


Solution 1:

Edited for Swift 4

Edit: Added @objc. While this isn't the best option for performance, one instance of it here shouldn't cause too many problems until there is a better solution.

Edited to fix when needing to interact with items behind GestureRecognizer.

Edit: Thanks @Rao for pointing this out. Added tap.cancelsTouchesInView = false.

This should help you with having multiple UITextView or UITextField

Create an extension of the view controller. This has worked much smoother for me and with less hassle than trying to use .resignFirstResponder()

extension UIViewController
{
    func setupToHideKeyboardOnTapOnView()
    {
        let tap: UITapGestureRecognizer = UITapGestureRecognizer(
            target: self,
            action: #selector(UIViewController.dismissKeyboard))

        tap.cancelsTouchesInView = false
        view.addGestureRecognizer(tap)
    }

    @objc func dismissKeyboard()
    {
        view.endEditing(true)
    }
}

Call self.setupToHideKeyboardOnTapOnView() in the viewDidLoad

Solution 2:

Try this, it's tested and working:

For Swift 3.0 / 4.0

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    self.view.endEditing(true)
}

For Older Swift

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent) {
    self.view.endEditing(true)
}

Solution 3:

swift 3

override func viewDidLoad() {
    super.viewDidLoad()
    self.view.addGestureRecognizer(UITapGestureRecognizer(target: self.view, action: #selector(UIView.endEditing(_:))))
}

Solution 4:

In this case, there is UITapGesture as one of the choices. I tried to create sample code just in case. Like this,

class ViewController: UIViewController {

    @IBOutlet weak var textField: UITextField!
    @IBOutlet weak var scrollView: UIScrollView!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        let tapGesture = UITapGestureRecognizer(target: self, action: "tap:")
        view.addGestureRecognizer(tapGesture)
    }

    func tap(gesture: UITapGestureRecognizer) {
        textField.resignFirstResponder()
    }
}

Solution 5:

Working Solution for Swift 3 that works with ScrollView

class ViewController: UIViewController {

    @IBOutlet weak var textField: UITextField!
    @IBOutlet weak var scrollView: UIScrollView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // The next line is the crucial part
        // The action is where Swift 3 varies from previous versions
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.tap(gesture:)))
        self.view.addGestureRecognizer(tapGesture)
    }

    func tap(gesture: UITapGestureRecognizer) {
        textField.resignFirstResponder()
    }
}

Another question that talks about this issue that I referenced and used. The accepted answer no longer works in Swift 3. The current selected answer should be the below answer.