How it works
AccessibilityFocusState is a property wrapper that lets your view observe and control where an assistive technology, such as VoiceOver or Switch Control, currently places its focus. It is the accessibility counterpart to FocusState: where FocusState tracks the keyboard focus a sighted user sees, AccessibilityFocusState tracks the element an assistive technology is reading or interacting with. Reach for it when you need to move the assistive cursor programmatically, for example to send a screen-reader user to a newly revealed field or to a validation error after a form submission.
Declare the focus binding with @AccessibilityFocusState
Annotate a stored property with
@AccessibilityFocusStateto create a binding whose value reflects, and can drive, the accessibility focus. The wrapped value is typically an optional matching a set of identifiers, so SwiftUI can represent the unfocused state asnil. Here@AccessibilityFocusState private var focus: Field?holds an optionalFieldvalue identifying which element accessibility focus rests on.Define a value type to identify focusable elements
Because the wrapped value compares against discrete cases, an enum makes a natural identifier for each focusable element. The
enum Field { case name, email }gives each text field a distinct tag, and the optionalField?letsfocusbenilwhen nothing is focused.Bind elements with accessibilityFocused(_:equals:)
Apply the
.accessibilityFocused(_:equals:)modifier to each view that should participate, passing the projected binding and the value that view represents. When accessibility focus lands on that element the binding takes on the matching value, and writing that value back moves focus to it. The firstTextFielduses.accessibilityFocused($focus, equals: .name)and the second uses.accessibilityFocused($focus, equals: .email), tying each field to itsFieldcase through$focus.Move accessibility focus by assigning to the wrapped value
To redirect an assistive technology programmatically, assign a new value to the property. SwiftUI then shifts the accessibility cursor to whichever element was bound to that value. The
Button("Focus Email")does exactly this withfocus = .email, which moves VoiceOver's focus onto the emailTextField.
focus = .email to focus = .name and run with VoiceOver enabled to watch accessibility focus jump to the Name field instead.Example & preview
Press Run live & edit to compile it in your browser — then edit the Swift on the left and the preview re-renders live.
struct AccessibilityFocusStateDemo: View {
enum Field { case name, email }
@AccessibilityFocusState private var focus: Field?
@State private var name = ""
@State private var email = ""
var body: some View {
VStack(spacing: 12) {
TextField("Name", text: $name)
.accessibilityFocused($focus, equals: .name)
TextField("Email", text: $email)
.accessibilityFocused($focus, equals: .email)
Button("Focus Email") { focus = .email }
}
.textFieldStyle(.roundedBorder)
.padding()
}
}