7

I have added a UITextField to UIAlertController, but shouldChangeCharactersInRange will get not fired. Why? I set the delegate.

let alertController = UIAlertController(title: "", message: "xxx", preferredStyle: .Alert)

self.presentViewController(alertController, animated:true, completion:nil)
let textField = UITextField()
textField.delegate = self
alertController.addTextFieldWithConfigurationHandler(nil)

and in the same class, the delegate:

func textField(textField: UITextField!,
    shouldChangeCharactersInRange range: NSRange,
    replacementString string: String!) -> Bool {
pkamb
  • 33,281
  • 23
  • 160
  • 191
János
  • 32,867
  • 38
  • 193
  • 353

2 Answers2

14

The text field that you're setting the delegate for is not the same text field that is added to the alert controller. Basically, you're creating a new instance of UITextField, but never giving it a frame, or adding it to the view hierarchy. At the same time, you're using addTextFieldWithConfigurationHandler() to add a text field to the alert controller, but you never set the delegate for this text field. I believe this is what you want:

let alertController = UIAlertController(title: "", message: "xxx", preferredStyle: .Alert)

alertController.addTextFieldWithConfigurationHandler {[weak self] (textField: UITextField!) in
    textField.delegate = self
}

self.presentViewController(alertController, animated:true, completion:nil)
Mick MacCallum
  • 129,200
  • 40
  • 280
  • 281
2

I was unable to get this working with UITextFieldDelegate. The delegate was set correctly but not being called for the UITextField within a UIAlertController.

Based on the answer here How do I validate TextFields in an UIAlertController? I learned you can instead use addTarget for UIControl.Event.editingChanged to call a selector when the editing changes.

let alertController = UIAlertController(title: "Title", message: "message", preferredStyle: .alert)

alertController.addTextField { (textField : UITextField!) -> Void in

    /*
     * Alternative to UITextFieldDelegate
     */
    textField.addTarget(alertController, action: #selector(alertController.textDidChange), for: .editingChanged)
}

let searchAction = UIAlertAction(title: "Search", style: .default)
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil )

alertController.addAction(searchAction)
alertController.addAction(cancelAction)

present(searchAlertController, animated: true)

You can extend or subclass UIAlertController to add the selector:

extension UIAlertController {

    @objc func textDidChange() {
        guard let textField = textFields?.first else { return }
        guard let searchAction = actions.first(where: { $0.title == "Search" }) else { return }
        let text = textField.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
        searchAction.isEnabled = !text.isEmpty
    }

}
pkamb
  • 33,281
  • 23
  • 160
  • 191