Giorgio
Giorgio

Reputation: 2210

UIAlertController with textfield: how dismiss keyboard works?

In my UIViewController subclass I created a UIAlertController with a UITextField:

class MyViewController: UIViewController {
...

     let alertController = UIAlertController(title: "a title", message: "", preferredStyle: .alert)
     let okAction = UIAlertAction(title: "OK!", style: .default) { alert in
          print("OK!!")
     }
     alertController.addTextField { (textField) in
     textField.placeholder = "a placeholder"
     textField.delegate = self
     }
     alertController.addAction(okAction)
     self.present(alertController, animated: true, completion: nil)

...

}

extension MyViewController: UITextFieldDelegate {
      func textFieldShouldReturn(_ textField: UITextField) -> Bool {
          return true
      }
}

Now, when alert is shown and I tap keyboard next/done button, the keyboard and the alert are dismissed and OK! is printed.

In my textFieldShouldReturn method there are neither textField.resignFirstResponder() nor alert dismiss command, so how it's possible that keyboard and alert is dismissed? How Apple achieves this behaviour? Has textField two delegates?

Upvotes: 1

Views: 1069

Answers (3)

Dominik Seemayr
Dominik Seemayr

Reputation: 889

As matt already mentioned, that behavior is normal for alerts including a textField. Once you hit the return-key on the keyboard it triggers .editingDidEndOnExit and that will dismiss the keyboard and the alert.

You can prevent that behavior by just removing the target on the textField like this:

  textField.removeTarget(nil, action: nil, for: .editingDidEndOnExit)

This will not dismiss the alert anymore once you tap the return-key on the keyboard.

Upvotes: 1

Giorgio
Giorgio

Reputation: 2210

I wanted to understand Apple UIAlertController behavior to add the same behavior in my alert custom controller that I'm implementing. With @matt's answer help, I think I figured out how UIAlertController works. Below my code snippet to connect all alert's textfields:

guard let last = textFields.last else {
    return
}
for i in 0 ..< textFields.count - 1 {
    textFields[i].returnKeyType = .next
    textFields[i].addTarget(textFields[i+1], action: #selector(UIResponder.becomeFirstResponder), for: .editingDidEndOnExit)
}
last.returnKeyType = .done
last.addTarget(last, action: #selector(UIResponder.resignFirstResponder), for: .editingDidEndOnExit)

In this way when I tap on keyboard next/done button and the last textfield is the first responder, keyboard is dismissed; otherwise next textfield becomes the first responder.

This post has helped too.

Upvotes: 0

matt
matt

Reputation: 535138

Actually, you're not putting the question strongly enough. You can delete your extension and the line textField.delegate = self, and the alert will still dismiss when the user hits Return.

That behavior is automatic for an alert. Unless you disable the dismiss button, it is tapped for you by default when the user hits Return in the virtual keyboard.

As for the actual mechanism, it's an editingDidEndOnExit control event. This leaves you free to set a delegate without messing things up.

Upvotes: 2

Related Questions