Adam Rush

@Adam9Rush

1 February, 2022

It’s a simple task that you’re likely to face when building your iOS application. Previously in SwiftUI, it was an awkward task to dismiss the keyboard.

In iOS 15, we saw the introduction of @FocusState a property wrapper that enables the focus state to be managed for focussing on elements on your application.

struct ContentView: View {
    @State private var email = ""
    @State private var password = ""
    @FocusState private var isFocused: Bool

    var body: some View {
        VStack {
            TextField("Email Address", text: $email)
                .focused($isFocused)
                .keyboardType(.emailAddress)
            SecureField("Password", text: $password)
                .focused($isFocused)
            
            Button("Login") {
                isFocused = false
            }
        }
    }
}

So, in the above example, we have two TextField’s that are used for a login flow, one containing the Email Address and the other is SecureTextField for the Password.

We can set the focused modifier to our isFocused state object, which will retain the state for this TextField.

On our login button, we can set the state to false, which will dismiss the keyboard.

Using Multiple SwiftUI TextFields

You might be required to dismiss the keyboard on different TextFields, especially if it’s a more significant data input application.

enum Field: Hashable {
        case email
        case password
    }

You can create a new enum that contains the field names.

Your @FocusState object is now going to reference the Field type.

@FocusState private var focusedField: Field?

Your following change makes our modifiers reference both the Field type and our field name.

TextField("Email Address", text: $email)
  .focused($focusedField, equals: .email)
  .keyboardType(.emailAddress)
SecureField("Password", text: $password)
  .focused($focusedField, equals: .password)

Finally, you need to check which field is active and set the active field on our login button action.

if email.isEmpty {
    focusedField = .email
} else if password.isEmpty {
    focusedField = .password
} else {
    // Process Login
}

You can see that hitting the login button will move focus to the next TextField, but have you noticed that nothing happens after the last Password, TextField?

This is because our else statement is empty to process the login. You’re likely going to dismiss the keyboard, and to do this, you can set focusedField to either false or nil.

if email.isEmpty {
    focusedField = .email
} else if password.isEmpty {
    focusedField = .password
} else {
    // Process Login
    focusedField = nil
}

You can now see the keyboard dismissing after the last TextField.

Supporting <iOS15

Unfortunately, I have some bad news; in SwiftUI, there is no simple way to dismiss the keyboard, there are some workarounds online, but it’s not something I will recommend doing. So, if you can support iOS 15+, I would always use this new property wrapper.

Sponsor

Subscribe for curated Swift content for free

- weekly delivered.