Hid
Hid

Reputation: 613

Code only works within a DispatchQueue.main.async block, why?

I wrote this code to select all the text when a user begins editing a UITextField:

@IBAction func onEditingBegin(_ sender: Any) {
    print("editing began")
    let textfield = sender as! UITextField
    textfield.selectAll(nil)
}

But it wouldn't work until I enclosed the textfield.selectAll(nil) line in a DispatchQueue.main.async block:

DispatchQueue.main.async {
    textfield.selectAll(nil)
}

Why is that?

I also printed out the name of the thread in onEditingBegin() and this was the result:

<NSThread: 0x60800006c880>{number = 1, name = main}

So it seems that it is already being fired on the main thread, but the code is still not working unless textfield.selectAll() is called inside of the DispatchQueue.main.async block.

Upvotes: 2

Views: 1089

Answers (1)

matt
matt

Reputation: 535306

The real effect of your call to DispatchQueue.main.async is to add a tiny, tiny delay. This delay, in particular, is exactly long enough to allow the current runloop to finish and the next runloop to start. The action that has caused an event to be sent to you, calling onEditingBegin, is thus permitted to complete. The text field now is editing, and so we are ready for the next step, namely to select its contents.

The trick you've discovered is actually something that is surprisingly often needed in iOS programming. Cocoa is a complicated framework, and manipulations of the interface sometimes can stumble over one another's feet, as here — while the user is starting to edit in the text field, you are trying to select the text field's text. Sometimes we just need the runloop to come around one more time in order to permit the interface to "settle down" before proceeding to the next step.

Upvotes: 6

Related Questions