The @FocusState property wrapper reads and writes which view has focus, paired with the
focused view modifier. Use it to move the cursor between fields, focus the first invalid
input, or dismiss the keyboard programmatically.
Boolean Bindings
Declare a @FocusState boolean and bind it with .focused($flag). SwiftUI flips it to true
when the user focuses the field and back to false when focus leaves. Writing the value moves
focus: set it to true to focus the field, or false to hide the keyboard.
struct SignInView: View {
@FocusState private var isEmailFocused: Bool
@FocusState private var isPasswordFocused: Bool
@State private var email = ""
@State private var password = ""
var body: some View {
Form {
TextField("email", text: $email)
.focused($isEmailFocused)
SecureField("password", text: $password)
.focused($isPasswordFocused)
Button("login") {
if email.isEmpty {
isEmailFocused = true
} else if password.isEmpty {
isPasswordFocused = true
} else {
isEmailFocused = false
isPasswordFocused = false
login()
}
}
}
}
}
One Enum for Many Fields
One boolean per field does not scale. @FocusState accepts any Hashable value, so model the
focusable fields as an enum and bind each field with .focused($focus, equals: .case). The
property must be optional, because sometimes nothing has focus; focus = nil dismisses the
keyboard.
enum FocusableField: Hashable {
case email
case password
}
struct ContentView: View {
@FocusState private var focus: FocusableField?
@State private var email = ""
@State private var password = ""
var body: some View {
Form {
TextField("email", text: $email)
.focused($focus, equals: .email)
SecureField("password", text: $password)
.focused($focus, equals: .password)
Button("login", action: login)
}
.toolbar {
ToolbarItem(placement: .keyboard) {
Button("next") {
if email.isEmpty { focus = .email }
else if password.isEmpty { focus = .password }
else { focus = nil }
}
}
}
.defaultFocus($focus, .email)
}
}
defaultFocus sets the focused field when the view appears. A ToolbarItem(placement: .keyboard) puts the next button above the keyboard.
Focusing the Search Field
searchFocused binds focus for the search bar a searchable modifier creates, with the same
boolean and Hashable forms. It only targets the search field of the current hierarchy, so one
enum can cover the results list and the search bar together.
SearchResultsView(results: store.results)
.focused($focus, equals: .results)
.searchable(text: store.$query)
.searchFocused($focus, equals: .search)