Episode #529

Controlling Focus in SwiftUI

11 minutes
Published on May 25, 2022

This video is only available to subscribers. Get access to this video and 584 others.

There are a number of types, propertyWrappers and view modifiers describing "focus" so it is not immediately obvious what they are all for. In this episode we will see how we can control focus for a text field in SwiftUI. We'll see how to use simple Bools as well as your own types to describe which field has focus. Finally we'll touch on a common request that doesn't yet have a great answer: setting focus in onAppear.

Example Form

struct LoginForm: View {
    enum Field: Hashable {
        case username
        case password
    }

    @State private var username = ""
    @State private var password = ""

    var body: some View {
        Form {
            TextField("Username", text: $username)

            SecureField("Password", text: $password)

            Button("Sign In") {
            }
        }
    }
}

Using a Boolean to control focus

We can start with a simple Bool property to control focus on our username field.

 @FocusState private var usernameFocused: Bool

Note that it does not (and cannot) take an initial value.

We use this property wrapper in conjunction with the focused view modifier.

TextField("Username", text: $username)
   .focused($usernameFocused)

Now this property wrapper will automatically be set when the field has focus.

We can also programmatically focus this field:

Button("Sign In") {
    if username.isEmpty {
        usernameFocused = true
    }
}

Using an Enum value to control focus

Using multiple Bool values to control focus on multiple fields is clunky. We can do better. Say we have an enum:

enum Field: Hashable {
    case username
    case password
}

We can use this with the @FocusState property wrapper, with one caveat: the value must be optional.

@FocusState var focusedField: Field?

We then pass a value to the focused() view modifier for each of our fields:

TextField("Username", text: $username)
    .focused($focusedField, equals: .username)

SecureField("Password", text: $password)
    .focused($focusedField, equals: .password)

Setting initial focus on appear

Now comes the most common request: setting focus to one of these fields when the view is first loaded. Unfortunately as of iOS 15 there is not yet a great solution. We’ll look at one a little later, but for now we have to resort to this:

.onAppear {
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
        focusedField = .username
    }
}

References

This episode uses Xcode 13.3, Swift 5.5.