63

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

White Hat
  • 681
  • 1
  • 6
  • 15

19 Answers19

116

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

Byron Coetsee
  • 3,533
  • 5
  • 20
  • 31
Matthew Bradshaw
  • 1,843
  • 3
  • 13
  • 21
  • 3
    Works beautifully and is elegant. This should be the answer – Bryan Norden Jul 25 '17 at 05:20
  • I had to add @objc func dismissKeyboard() in Swift 4 – MCR Oct 30 '17 at 14:52
  • 1
    @MCR you are correct. I updated the answer to reflect that until I can find a more definitive approach. – Matthew Bradshaw Oct 30 '17 at 15:06
  • 6
    Caution: If you have say, a tableView on the same view, this code will block the cell tap and your didSelectRow won't trigger. In order to avoid this you will need to add `tap.cancelsTouchesInView = false` before `addGestureRecognizer` – Nagendra Rao Mar 12 '18 at 20:38
  • Does anyone know how to prevent `dismissKeyboard()` from being called if the tap was done in a view that is embedded in a `UIViewContainer` in self? – BadmintonCat Oct 09 '18 at 19:06
61

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)
}
Gaurav Gandhi
  • 3,041
  • 2
  • 27
  • 40
iAnurag
  • 9,286
  • 3
  • 31
  • 48
17

swift 3

override func viewDidLoad() {
    super.viewDidLoad()
    self.view.addGestureRecognizer(UITapGestureRecognizer(target: self.view, action: #selector(UIView.endEditing(_:))))
}
Giang
  • 3,553
  • 30
  • 28
12

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()
    }
}
pixyzehn
  • 762
  • 6
  • 15
  • Thanks it worked. But I have another problem, I have a view controller with 5 textfields. is there a good way to do the same without calling "resignFirstResponder" method for them one by one? – White Hat Aug 29 '15 at 02:10
  • another question, is there any way dismiss the keyboard when I tap any where outside the textfield? for example when I tap on a button? – White Hat Aug 29 '15 at 02:15
  • @white-hat Could you ask that question as a another question? – pixyzehn Aug 30 '15 at 13:54
  • @WhiteHat Put your 5 text fields in an outlet collection. Then create a dismissKeyboard method that iterates over the collection and calls resignFirstResponder on each text field. You can call this method from both the gesture recognizer and the button actions. – bugloaf Jul 11 '16 at 15:01
  • @WhiteHat ViewController.View could call endEdting – Steven Jiang Dec 23 '16 at 02:58
11

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.

Community
  • 1
  • 1
Devbot10
  • 1,193
  • 18
  • 33
  • @JoeBlow I believe you are wrong. You are supposed to be resigning the first responder of the textField. Not the view. I have this code in my current project, and just tested it in a new project. Not to mention, my answer is just a newer version of the answer written by Pixyzehn. – Devbot10 Jan 20 '17 at 20:48
  • 1
    hey dev @Devbot10 - don't hesitate to edit it back to what you had before man. you know, I may have been confusing with `.endEditing`, which indeed recurses through all views below. – Fattie Jan 21 '17 at 12:09
5

Details

  • Xcode 10.2.1 (10E1001), Swift 5

Solution 1

endEditing(_:)

let gesture = UITapGestureRecognizer(target: tableView, action: #selector(UITextView.endEditing(_:)))
tableView.addGestureRecognizer(gesture)

Usage of solution 1. Full sample

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        let textField = UITextField(frame: CGRect(x: 50, y: 50, width: 200, height: 30))
        textField.borderStyle = .roundedRect
        textField.placeholder = "Enter text"
        textField.becomeFirstResponder()
        view.addSubview(textField)
        let gesture = UITapGestureRecognizer(target: view, action: #selector(UIView.endEditing(_:)))
        view.addGestureRecognizer(gesture)
    }
}

Solution 2

class TapGestureRecognizer

import UIKit

class TapGestureRecognizer: UITapGestureRecognizer {

    let identifier: String

    init(target: Any?, action: Selector?, identifier: String) {
        self.identifier = identifier
        super.init(target: target, action: action)
    }

    static func == (left: TapGestureRecognizer, right: TapGestureRecognizer) -> Bool {
        return left.identifier == right.identifier
    }
}

extension UIView

import UIKit

extension UIView {

    private var hideKeybordOnTapIdentifier: String { return "hideKeybordOnTapIdentifier" }

    private var hideKeybordOnTapGestureRecognizer: TapGestureRecognizer? {
        let hideKeyboardGesture = TapGestureRecognizer(target: self, action: #selector(UIView.hideKeyboard),
                                                       identifier: hideKeybordOnTapIdentifier)
        if let gestureRecognizers = self.gestureRecognizers {
            for gestureRecognizer in gestureRecognizers {
                if let tapGestureRecognizer = gestureRecognizer as? TapGestureRecognizer,
                    tapGestureRecognizer == hideKeyboardGesture {
                    return tapGestureRecognizer
                }
            }
        }
        return nil
    }

    @objc private func hideKeyboard() { endEditing(true) }

    var hideKeyboardOnTap: Bool {
        set {
            let hideKeyboardGesture = TapGestureRecognizer(target: self, action: #selector(hideKeyboard),
                                                           identifier: hideKeybordOnTapIdentifier)
            if let hideKeybordOnTapGestureRecognizer = hideKeybordOnTapGestureRecognizer {
                removeGestureRecognizer(hideKeybordOnTapGestureRecognizer)
                if gestureRecognizers?.count == 0 { gestureRecognizers = nil }
            }
            if newValue { addGestureRecognizer(hideKeyboardGesture) }
        }
        get { return hideKeybordOnTapGestureRecognizer == nil ? false : true }
    }
}

Usage of solution 2

view.hideKeyboardOnTap = true

Solution 2 full Sample

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        let textField = UITextField(frame: CGRect(x: 50, y: 50, width: 200, height: 30))
        textField.borderStyle = .roundedRect
        textField.placeholder = "Enter text"
        textField.becomeFirstResponder()
        view.addSubview(textField)
        view.hideKeyboardOnTap = true
    }
}
Vasily Bodnarchuk
  • 24,482
  • 9
  • 132
  • 127
5

Check this out.

override func viewDidLoad() {
    var tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.handleTap))
    self.view.userInteractionEnabled = true
    self.view.addGestureRecognizer(tapGesture)
}

Then your tap handler is.

  func handleTap(sender: UITapGestureRecognizer) {
    self.view.endEditing(true)
}
handiansom
  • 783
  • 11
  • 27
3

For Swift 3

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

This works when touched outside input area for any number of input items.

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
        self.view.endEditing(true)
    }
Venkat Ram
  • 41
  • 3
1

I had the same problem and i finally solved it !

Set a TapGestureRecognizer in your Storyboard and then an Outlet in your ViewController

@IBOutlet var tapGesture: UITapGestureRecognizer!

Then set an IBAction in your ViewController

@IBAction func DismissKeyboard(sender: UITapGestureRecognizer)
{
 self.view.endEditing(true)
} 

add these lines to your viewDidLoad method

override func viewDidLoad()
{
    super.viewDidLoad()
    self.view.addGestureRecognizer(tapGesture)
}

and its should work

Hope that will help !

Lynkz7
  • 67
  • 9
1

Every touch different of text field dismiss the keyboard or use resignfirstresponder

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
 UITouch *touch = [touches anyObject];
 if(![touch.view isMemberOfClass:[UITextField class]]) {
     [touch.view endEditing:YES];
 }
}
1
func findAndResignFirstResponder(_ stView: UIView) -> Bool {

    if stView.isFirstResponder {
        stView.resignFirstResponder()
        return true
    }
    for subView: UIView in stView.subviews {
        if findAndResignFirstResponder(subView) {
            return true
        }
    }
    return false
}
Subhajit
  • 3
  • 1
0

I created this method in Obj-C that hides a keyboard no matter where the user is currently typing:

//call this method
+ (void)hideKeyboard {
    //grab the main window of the application
    UIWindow *window = [UIApplication sharedApplication].keyWindow;
    //call our recursive method below
    [self resignResponderForView:window];
}

//our recursive method
+ (void)resignResponderForView:(UIView *)view {
    //resign responder from this view
    //If it has the keyboard, then it will hide the keyboard
    [view resignFirstResponder];
    //if it has no subviews, then return back up the stack
    if (view.subviews.count == 0)
        return;
    //go through all of its subviews
    for (UIView *subview in view.subviews) {
        //recursively call the method on those subviews
        [self resignResponderForView:subview];
    }
}

I hope that that is translate-able into Swift and makes sense. It can be called anywhere in the application and will hide the keyboard no matter what VC you're on or anything.

AlexKoren
  • 1,605
  • 16
  • 28
  • 1
    You might want to check out [`-[UIView endEditing:]`](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/#//apple_ref/occ/instm/UIView/endEditing:). – rob mayoff Oct 27 '15 at 16:17
0

Go to Keyboard Type and Select Default or whatever you need the TextField for. Then override a method call it whatever you want i usually call it touchingBegins. Below is what you forgot to add

super.touchingBegins(touches, withEvent: event)
 }
Katz
  • 826
  • 3
  • 19
  • 40
0

Introduce a tap gesture recogniser and set and action for it.

Use the code:

nameofyourtextfield.resignfirstresponder()

kuni
  • 16
0

//In swift 4..It worked for me.

func setupKeyboardDismissRecognizer(){
    let tapRecognizer: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(searchingActivity.dismissKeyboard))

    self.view.addGestureRecognizer(tapRecognizer)
}

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

//Call this function setupKeyboardDismissRecognizer() in viewDidLoad

Raghib Arshi
  • 717
  • 8
  • 12
0

In Swift4

 override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
            self.view.endEditing(true)
        }
Raghib Arshi
  • 717
  • 8
  • 12
0

In Swift 4 or 5 You can use like..

class ViewController: UIViewController {

  override func viewDidLoad() {
    super.viewDidLoad()

    //Key borad dismiss
    let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "dismissKeyboard")
    tap.cancelsTouchesInView = false
    view.addGestureRecognizer(tap)
  }

 //Key board hide on outside the textfield
 @objc func dismissKeyboard() {
    //Causes the view (or one of its embedded text fields) to resign the first responder status.
    view.endEditing(true)
  }
}
Enamul Haque
  • 4,789
  • 1
  • 37
  • 50
0

I used the following code in Swift 4, you can try this,

override func viewDidLoad() {
    super.viewDidLoad()

    // dismiss keyboard when tap outside a text field 
    let tapGestureRecognizer: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(YourViewControllerName.dismissKeyboard))
    view.addGestureRecognizer(tapGestureRecognizer)
}

//Calls this function when the tap is recognized.
func dismissKeyboard() {
    //Causes the view (or one of its embedded text fields) to resign the first responder status.
    view.endEditing(true)
}
Varun P V
  • 1,092
  • 1
  • 12
  • 30