@FocusState — what it is, managing focus and keyboard in SwiftUI

Author: IT Sectr Published: 2026-06-26 Reading time: 9 min

@FocusState is a property wrapper in SwiftUI, introduced in iOS 15, that allows you to programmatically control input focus on text fields and other elements. Before its introduction, developers had to use UIViewRepresentable to access UIKit methods becomeFirstResponder and resignFirstResponder. @FocusState solves this problem natively: you bind a property to a field via the .focused() modifier, after which setting or clearing focus is done with a simple value assignment. According to Apple Developer Documentation — FocusState (2025), @FocusState supports two modes: Bool for simple management (focus is on or off) and enum for multiple fields, where each case corresponds to a specific input field.

Key Takeaways

  • @FocusState — a property wrapper for programmatic focus management in SwiftUI, available since iOS 15.
  • Bool Mode — for a single field use @FocusState var isFocused: Bool with .focused($isFocused).
  • Enum Mode — for multiple fields use an enum conforming to FocusStateValue and .focused($field, equals: .fieldName).
  • Hiding the Keyboard — set focus to nil or false to dismiss the keyboard.
  • Automatic Focus — set the initial value in .onAppear to show the keyboard when the screen opens.

What is @FocusState in SwiftUI

@FocusState is a property wrapper that ties the focus state to a specific input field or other focusable element in SwiftUI. Unlike UIKit, where focus management happens through becomeFirstResponder and resignFirstResponder methods, SwiftUI uses a declarative approach: you declare a state (@FocusState) and bind it to an element via the .focused() modifier. Changing the state automatically changes the focus.

Before @FocusState was introduced in iOS 15, developers had to create UIViewRepresentable wrappers around UITextField or use third-party libraries. @FocusState is integrated directly into SwiftUI and works with TextField, TextEditor, SecureField, and SearchField. This makes code cleaner, reduces the number of UIKit bridges, and improves testability.

According to WWDC Session 10136 — What's new in SwiftUI (2024), @FocusState uses SwiftUI's preference key system to pass focus information between elements. When a field receives focus, SwiftUI automatically updates the associated @FocusState property, allowing you to react to focus changes in code.

Managing Focus with Bool

The simplest way to use @FocusState is with the Bool type. When a field is focused, the property is true. When focus is lost — false. You can force focus by setting it to true, or clear it by setting it to false.

swift
struct LoginForm: View {
    @State var email = ""
    @FocusState var isEmailFocused: Bool
    
    var body: some View {
        VStack {
            TextField("Email", text: $email)
                .focused($isEmailFocused)
            
            Button("Show Keyboard") {
                isEmailFocused = true
            }
            Button("Hide Keyboard") {
                isEmailFocused = false
            }
        }
    }
}

In this example, isEmailFocused automatically becomes true when the user taps on the text field, and false when the keyboard is dismissed. The buttons allow you to manage focus programmatically — useful for custom keyboards, "Next" buttons, and situations where you need to force-dismiss the keyboard after form submission.

Managing Focus with Enum for Multiple Fields

For forms with multiple fields, @FocusState supports an enum conforming to the FocusStateValue (or Hashable) protocol. Each enum case corresponds to a specific field. This allows switching focus between fields — for example, when the user presses "Next" on the keyboard to move to the next field.

swift
struct RegistrationForm: View {
    enum Field: Hashable {
        case email
        case password
        case confirmPassword
    }
    
    @State var email = ""
    @State var password = ""
    @State var confirmPassword = ""
    @FocusState var focusedField: Field?
    
    var body: some View {
        Form {
            TextField("Email", text: $email)
                .focused($focusedField, equals: .email)
                .onSubmit { focusedField = .password }
            
            SecureField("Password", text: $password)
                .focused($focusedField, equals: .password)
                .onSubmit { focusedField = .confirmPassword }
            
            SecureField("Confirm", text: $confirmPassword)
                .focused($focusedField, equals: .confirmPassword)
                .onSubmit { submitForm() }
        }
    }
}

Note the .onSubmit modifier — it is called when the user presses "Return" on the keyboard. Inside .onSubmit we switch focusedField to the next field, which automatically moves focus. The last field calls submitForm() to submit the form.

Hiding and Showing the Keyboard

@FocusState provides a simple way to hide the keyboard — just set the property to nil (for enum) or false (for Bool). However, sometimes you need to hide the keyboard without tying it to a specific field — for example, when tapping on empty space. In this case, there are several approaches.

swift
struct DismissKeyboardView: View {
    @State var text = ""
    @FocusState var isFocused: Bool
    
    var body: some View {
        TextField("Enter text", text: $text)
            .focused($isFocused)
            .toolbar {
                ToolbarItemGroup(placement: .keyboard) {
                    Spacer()
                    Button("Done") {
                        isFocused = false
                    }
                }
            }
    }
}

The .toolbar modifier with placement .keyboard adds a button above the keyboard. This is a standard UX pattern in iOS for dismissing the keyboard. An alternative approach is to use .onTapGesture on the root VStack to reset focus when tapping on the background.

Focus and Form Validation

@FocusState combines perfectly with form validation. A typical pattern: after pressing the "Submit" button, validate all fields and set focus to the first field with an error. This improves the user experience — the user immediately sees which field needs to be corrected.

swift
struct ValidatedForm: View {
    enum Field: Hashable { case name; case phone }
    
    @State var name = ""
    @State var phone = ""
    @FocusState var focusedField: Field?
    @State var errors: [String] = []
    
    var body: some View {
        Form {
            TextField("Name", text: $name)
                .focused($focusedField, equals: .name)
            TextField("Phone", text: $phone)
                .focused($focusedField, equals: .phone)
            
            Button("Submit") { validateAndSubmit() }
        }
    }
    
    func validateAndSubmit() {
        if name.isEmpty {
            focusedField = .name
            return
        }
        if phone.isEmpty {
            focusedField = .phone
            return
        }
        // submit form
    }
}

In this example, if the name field is empty, focus moves to it, and the user immediately sees where the error is. If name is filled, phone is checked. This is natural behavior for forms — the user fills fields from top to bottom, and validation follows the same order.

Common Mistakes with @FocusState

The most common mistake is trying to use @FocusState with a type that does not conform to Hashable. @FocusState requires the property type to be Hashable (Bool and optional enums already conform). If you are trying to use a custom structure, make sure it implements Hashable.

  • Forgot .focused() modifier — @FocusState on its own does not manage focus. You must bind it to a field via .focused($property) or .focused($property, equals: .case).
  • Multiple @FocusState in one View — for multiple fields, use a single @FocusState with an enum, not multiple @FocusState properties. Multiple Bool properties will not be synchronized with each other.
  • Changing @FocusState off the main thread — @FocusState should only be changed on the main thread, like all UI properties in SwiftUI. Asynchronous operations must switch to MainActor before changing it.
  • Focus reset on view rebuild — if the View is rebuilt, @FocusState may reset. Use the .id() modifier for stable View identification.
swift
// ❌ Wrong: two @FocusState Bool instead of enum
@FocusState var isNameFocused: Bool
@FocusState var isEmailFocused: Bool

// ✅ Correct: single enum @FocusState
enum Field: Hashable { case name; case email }
@FocusState var focusedField: Field?

Frequently Asked Questions

Which iOS versions support @FocusState?

@FocusState is available from iOS 15, iPadOS 15, macOS 12, tvOS 15, and watchOS 8. For projects supporting iOS 14 and below, use UIViewRepresentable with UITextField and becomeFirstResponder, or third-party libraries with custom focus management implementation.

Can I use @FocusState with custom UIViewRepresentable?

Yes, you need to implement FocusState support in the custom UIViewRepresentable through the UIViewRepresentable protocol. The custom view must have becomeFirstResponder and resignFirstResponder. SwiftUI will automatically link @FocusState with these methods if you specify the .focused() modifier.

Why doesn't @FocusState work with TextField in List?

In List or Form, cells can be reused, which breaks the @FocusState binding with the field. Solution: add the .id() modifier with a unique identifier for each TextField. For example: .id(fieldName). This forces SwiftUI to create a separate View instance for each field.

How to hide the keyboard when tapping on empty space?

Add .onTapGesture to the root container (VStack, ZStack) and reset focus: focusedField = nil. However, .onTapGesture may block taps on buttons inside — use a container with .contentShape(Rectangle()) and .onTapGesture on it, or a custom UIKitBackgroundView.

How to animate keyboard appearance with @FocusState?

@FocusState does not provide a direct API for keyboard animation — this is iOS system behavior. However, you can react to focus changes with .onChange(of: focusedField) or .onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillShowNotification)) for custom content animation.

Summary

  • @FocusState — a native SwiftUI property wrapper for input focus management, available since iOS 15.
  • Two modes — Bool for a single field, Hashable enum for multiple form fields.
  • .focused() modifier — required to bind @FocusState to a specific input field.
  • Programmatic control — setting the value to nil or false dismisses the keyboard.
  • Form validation — @FocusState allows setting focus on the first field with an error after validation.
  • Enum for multiple fields — a single @FocusState with enum is preferable to multiple Bool properties.
  • iOS 15+ — for older versions, use UIViewRepresentable with becomeFirstResponder.

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also