Using text fields in our apps is pretty common; it's the way for users to input custom text. They often simply tap on the text field they want to type into, and the keyboard appears. But there are times where a text field must be selected programmatically without any user interaction. That's where things get interesting, as the first years of SwiftUI it was tricky to do that; introducing UIKit text fields in SwiftUI was the only option for developers. However, as of iOS 15, there's been a SwiftUI native way to programmatically control focus on text fields, the @FocusState property wrapper.
@FocusState
makes it really easy to manage text field focus programmatically, but it might look a bit unclear how to put it in motion at first. In this post we'll get to know how to properly use it, no matter if there's a single, or many text fields in a view.
Focusing on a single text field
Let's get started with a simple SwiftUI view, where there is only a text field and a button:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
struct SingleTextFieldView: View { @State private var text = "" var body: some View { VStack { TextField("Type something", text: $text) .textFieldStyle(.roundedBorder) Button("Focus") { } .padding(.top, 30) } .padding() } } |
The purpose is to make the text field active and present the keyboard just by tapping on the button. Managing that includes three distinct steps:
At first, we need to declare a property in the view, marked with the @FocusState
property wrapper. Note that this property:
- Does not have a type.
- Does not have explicit initial value.
1 2 3 |
@FocusState private var isFocused |
Consider isFocused
as a Bool
value with its initial value implicitly set to false
by default. The name can be anything we want.
The second step is to toggle its value and make it true
right where it's necessary to trigger the keyboard appearance. In this case, that's the action closure of the button:
1 2 3 4 5 |
Button("Focus") { isFocused = true } |
Lastly, we need to apply a specific view modifier to the text field, called focused
. As argument we provide the binding value of the isFocused
property:
1 2 3 4 |
TextField("Type something", text: $text) .focused($isFocused) |
That's all it always takes to programmatically focus on a text field in SwiftUI using the @FocusState
property wrapper. With the above additions, the initial view is updated like so:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
struct SingleTextFieldView: View { @State private var text = "" @FocusState private var isFocused var body: some View { VStack { TextField("Type something", text: $text) .textFieldStyle(.roundedBorder) .focused($isFocused) Button("Focus") { isFocused = true } .padding(.top, 30) } .padding() } } |
To programmatically dismiss the keyboard, we just need to make the isFocused
property false
again. But on top of that, we can also use isFocused
for conditional code execution, as you can see in the next addition to the view; a button to dismiss the keyboard appears if only isFocused
is true
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
struct SingleTextFieldView: View { @State private var text = "" @FocusState private var isFocused var body: some View { VStack { ... if isFocused { Button("Dismiss Keyboard") { isFocused = false } .padding(.top, 20) } } .padding() } } |
Here's how all the above work:

Focusing on a text field when the view appears
Sometimes we want a text field to have the focus right when the view appears, so the keyboard appears instantly and users can start typing at once. For example, take a look at the following two views, where a navigation link in the first one navigates to the second that contains a text field:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
struct FocusOnAppearView: View { var body: some View { NavigationStack { NavigationLink(destination: TextFieldView()) { Text("Show text field view") } } } } struct TextFieldView: View { @State private var text = "" @FocusState private var isFocused var body: some View { VStack { TextField("Write something", text: $text) .textFieldStyle(.roundedBorder) .focused($isFocused) } .padding() } } |

See that the TextFieldView
already contains a @FocusState
property and the focused
modifier applied on the text field.
At the moment, the keyboard does not show up automatically, nor the text field is activated when the view appears. However, managing that is as simple as it sounds; we just need to toggle the value of isFocused
in the onAppear
modifier. The latter is called right upon the view appearance.
With that in mind, we can update the TextFieldView
by appending the onAppear
modifier as shown next:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
struct TextFieldView: View { @State private var text = "" @FocusState private var isFocused var body: some View { VStack { TextField("Write something", text: $text) .textFieldStyle(.roundedBorder) .focused($isFocused) } .padding() .onAppear { isFocused = true } } } |
The text field now gains the focus automatically and the keyboard is presented right when the view appears:

Focusing on two text fields subsequently
SwiftUI views and forms often have more than one text fields, where it might be necessary to programmatically focus on one text field after the other. To talk in code, see the following example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
struct DoubleTextFieldsView: View { @State private var firstname = "" @State private var lastname = "" @FocusState private var isFirstNameFocused @FocusState private var isLastNameFocused var body: some View { VStack(spacing: 20) { TextField("First name", text: $firstname) .focused($isFirstNameFocused) TextField("Last name", text: $lastname) .focused($isLastNameFocused) Button("Next") { } } .padding() .textFieldStyle(.roundedBorder) } } |
The view contains two text fields, and two @FocusState
properties respectively. The focused
modifier is applied on each text field, getting as argument the binding value of the proper @FocusState
property ($isFirstNameFocused
and $isLastNameFocused
).
It's made instantly clear here that it's perfectly fine for a view to contain two @FocusState
properties. In fact, it's perfectly fine to contain any number of them. But when they become more than two or three, managing them starts to be difficult and risky.
☝️ Note:
There's a better way to manage multiple @FocusState
properties for multiple text fields in the next part.
The view also contains the Next button. If no text view has the focus, this button will activate the first one. Otherwise, it will switch the focus between text fields simply by toggling the values of the @FocusState
properties.
Let's add the code that will do all that:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
struct DoubleTextFieldsView: View { @State private var firstname = "" @State private var lastname = "" @FocusState private var isFirstNameFocused @FocusState private var isLastNameFocused var body: some View { VStack(spacing: 20) { ... Button("Next") { if !isFirstNameFocused && !isLastNameFocused { isFirstNameFocused = true } else { isFirstNameFocused.toggle() isLastNameFocused.toggle() } } } .padding() .textFieldStyle(.roundedBorder) } } |

Notice that the Next button is working as intended, and the two @FocusState
properties have their values updated as needed. However, the keyboard does not remain constantly present, even though the text fields get the focus one after the other instantly. That kind of flickering can be annoying, but it can be avoided as described in the next part. In any case, it remains the fact here that we can perfectly have more than one @FocusState
properties in a view.
☝️ Note:
Actually, it's possible to avoid flickering by setting true
to both @FocusState
properties simultaneously, instead of toggling them in the Next button:isFirstNameFocused
= true
isLastNameFocused = true
However, this might lead to unexpected behavior and I would avoid it. The following method is much better to get around any visual inconsistencies.
Focusing on multiple text fields
Managing multiple focus states when there are multiple text fields can become pretty hard, with messy and complicated code. However, things become straightforward if we use an enum with cases describing the text field that should have the focus, instead of multiple @FocusState
properties.
To make that clear, let's see first the following SwiftUI view that contains multiple text fields:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
struct MultipleTextFieldsView: View { @State private var firstname = "" @State private var lastname = "" @State private var username = "" @State private var password = "" var body: some View { VStack(spacing: 20) { TextField("First name", text: $firstname) TextField("Last name", text: $lastname) TextField("User name", text: $username) SecureField("Password", text: $password) Button("Next") { } } .padding() .textFieldStyle(.roundedBorder) } } |
Right now there aren't any @FocusState
properties, but the Next button is present again. Its purpose is to rotate the focus from one text field to the other, but so far it does nothing at all.
To make it possible to programmatically give the focus to multiple text fields, we start by declaring an enum in the view as shown next:
1 2 3 4 5 6 7 8 9 |
struct MultipleTextFieldsView: View { enum FocusedField { case firstname, lastname, username, password } ... } |
The name of the enum can be anything. Notice that there are four cases, just as many as the text fields in the view. Moreover, each case name matches to a @State
property, but note that it's not necessary to use similar names. I did it here for clarity reasons.
Next, we declare just one @FocusState
property, following one rule; we explicitly set the enum type as its datatype, and we keep it optional:
1 2 3 |
@FocusState private var focusedField: FocusedField? |
After that, and just like we previously did, we'll apply the focused
modifier on all text fields, but with an additional argument as you see next:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
TextField("First name", text: $firstname) .focused($focusedField, equals: .firstname) TextField("Last name", text: $lastname) .focused($focusedField, equals: .lastname) TextField("User name", text: $username) .focused($focusedField, equals: .username) SecureField("Password", text: $password) .focused($focusedField, equals: .password) |
The focus(_:equals:)
modifier checks if the current value of the focusedField
is equal to the second argument (also an enum case), and if so, it gives the focus on the text field. But In order to make this comparison possible, the enum must conform to Hashable
protocol, and simple enums like the FocusedField
do so by default.
With all these additions, we can add now the missing functionality to the Next button. When it's tapped, it'll start from the first text field and will continue giving the focus on the next one in a rotation, simply by setting the appropriate value to focusedField
property:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
Button("Next") { switch focusedField { case .firstname: focusedField = .lastname case .lastname: focusedField = .username case .username: focusedField = .password case .password: focusedField = .firstname case nil: focusedField = .firstname } } |
Finally, let's add another button to dismiss the keyboard. This one will be conditionally visible and will perform a specific task; to make focusedField
nil
again, so no text field is active and let the keyboard disappear:
1 2 3 4 5 6 7 |
if focusedField != nil { Button("Dismiss Keyboard") { focusedField = nil } } |
The entire view implementation is the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
struct MultipleTextFieldsView: View { enum FocusedField { case firstname, lastname, username, password } @State private var firstname = "" @State private var lastname = "" @State private var username = "" @State private var password = "" @FocusState private var focusedField: FocusedField? var body: some View { VStack(spacing: 20) { TextField("First name", text: $firstname) .focused($focusedField, equals: .firstname) TextField("Last name", text: $lastname) .focused($focusedField, equals: .lastname) TextField("User name", text: $username) .focused($focusedField, equals: .username) SecureField("Password", text: $password) .focused($focusedField, equals: .password) Button("Next") { switch focusedField { case .firstname: focusedField = .lastname case .lastname: focusedField = .username case .username: focusedField = .password case .password: focusedField = .firstname case nil: focusedField = .firstname } } if focusedField != nil { Button("Dismiss Keyboard") { focusedField = nil } } } .padding() .textFieldStyle(.roundedBorder) } } |

Conclusion
Focusing programmatically either on a single, or multiple text fields in a view is a process that always requires the same series of steps. Through the previous parts you've met how to make them active without any user interaction, and how to also dismiss the keyboard programmatically. My advice is to always prefer the enum-based approach when the view contains more than one text fields, even when they're just two of them, unless there are specific reasons not to do that. If @FocusState
has been confusing you, then I hope this post helped you figure out how to master it. Thanks for reading!