Gallaugher Gallaugher - 2 months ago 7
Swift Question

Can't set UITextField's selectedTextRange if alert is presented, first

I'm running the code below when the user has an input error. If an error occurs, an alert is displayed and text in the UITextField is supposed to be selected. If I run my alert function showErrorAlert() before selecting the text (as depicted below), the text is never selected. If I comment out the alert function, the text selection seems to work fine (selecting the entire text in the input field). I'm not sure what I need to do to get the selectedTextRange to select properly after calling my alert function. Thanks for tolerating a n00b question.

// showErrorAlert Function

func showErrorAlert(title: String, message: String) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)

let defaultAction = UIAlertAction(title: "OK", style: .default, handler: nil)
alertController.addAction(defaultAction)

present(alertController, animated: true, completion: nil)

}


// When input error occurs…

showErrorAlert(title: "ERROR", message: "Please type numbers, only.")

inputText.becomeFirstResponder()
let textBeginning = inputText.beginningOfDocument
let textEnd = inputText.endOfDocument
inputText.selectedTextRange = inputText.textRange(from: textBeginning, to: textEnd)

Answer

Try placing your selection code inside the completion handler of your defaultAction. This way selection will take place after your user acknowledges and dismisses the alert. Like so:

func showErrorAlert(title: String, message: String) {
    let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)

    let defaultAction = UIAlertAction(title: "OK", style: .default) { (action) in
        self.textInput.becomeFirstResponder()
        let textBeginning = self.textInput.beginningOfDocument
        let textEnd = self.textInput.endOfDocument
        self.textInput.selectedTextRange = self.textInput.textRange(from: textBeginning, to: textEnd)
    }
    alertController.addAction(defaultAction)

    present(alertController, animated: true, completion: nil)
}

I just tested this on a device.

Explanation

When you call present(alertController...) what you are doing is presenting a new view controller modally. In your original code, you called showErrorAlert which then presents a new view controller (which in this case happens to be a UIViewController subclass called UIAlertController). That's totally fine.

But a problem occurs when you then--immediately after presenting this new view controller--try to select text in a UITextField that is not a subview of the currently presented view. A way to fix this is to call this selection code in the completion handler of your defaultAction. This ensures that your selection code will be executed only after the alert is dismissed and when your UITextField is back as a subview of the currently presented view.

Comments