iosswiftuitextfieldtap

Tapping outside a UITextField to stop editing


When I tap outside my UITextField to clear it's content and hide the keyboard it reacts as if I hit return and triggers actions associated with hitting return. It seems pretty counterintuitive to me.

The textField cleared and keyboard dismissed I want, but not amountEntered(_:) called.. I want to be able to abort any action when I tap outside the textField

How can I change that?

enter image description here

All the code related to the TextField:

override func viewDidLoad() {
    super.viewDidLoad()

    amountEnteredTextField.delegate = self

    registerForKeyboardNotifications()
    self.hideKeyboard()

}

func textFieldShouldReturn(_ scoreText: UITextField) -> Bool {
    self.view.endEditing(true)
    return true
}

@IBAction func amountEntered(_ sender: UITextField) {

    if allowNewAmount == true {

        if amountEnteredTextField.text == "" {
            return
        }

        amountLabel.text = amountEnteredTextField.text

        amountEnteredTextField.text = ""

    }
}

After debugging, the issue seems to be coming from this extension used to hide the keyboard:

extension UIViewController {

    func hideKeyboard() {

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

        view.addGestureRecognizer(tap)
    }

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

Solution

  • So it seems your amountEntered(_:) is called anytime amountEnteredTextField ends editing. This is due to you setting it up as an IBAction for the event .editingDidEnd.

    In hideKeyboard(), you have set up a tap gesture on self.view linked to dismissKeyboard() that does view.endEditing(true). This basically resigns any first responder; meaning amountEnteredTextField editing ends and hence amountEntered(_:) is called when you tap outside.

    If you want to ensure amountEntered(_:) is called only when the user taps on the keyboard's return key then you should modify your design a little bit as so:

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        /*
         Check to see which is the delegated textField.
         Basically to do textField-specific logic.
         Helps mostly if you have multiple textFields
         */
        if textField == amountEnteredTextField {
            amountEntered()
        }
    
        //resign textField
        textField.resignFirstResponder()
        return true
    }
    
    func amountEntered() {
        if allowNewAmount, amountEnteredTextField.text != "" {
            amountLabel.text = amountEnteredTextField.text
            amountEnteredTextField.text = ""            
        }
    }