Control accessibility focus in iOS apps with `@AccessibilityFocusState`

Gennadii Tsypenko
Just Eat Takeaway-tech
3 min readDec 12, 2023

Are you familiar with the situation: VoiceOver is on, you close the bottom sheet (after you entered a phone number or picked a date) and the focus moves all the way to the very top of the screen, possibly the back button.

Now, you need to go through all the elements on the screen again from top to bottom until you reach the previous state. Depending on the screen it can be tedious and frustrating.

Photo by Ethan Johnson on Unsplash

It can and should be better than this. Here is how Apple defines accessibility focus in the official documentation:

VoiceOver and other assistive technologies place a virtual focus on elements, which allows users to inspect an element without activating it. If you know the current location of the virtual focus, you can optimize the user experience for assistive technology users. For example, if your application expects people to tap once to select an object and then double-tap to activate it, VoiceOver users must make an extra tap to focus VoiceOver on the object before tapping to select it. To improve the VoiceOver user’s experience, you can move selection to an element at the same time VoiceOver focuses on the element. In this way, the user can activate the element without having to tap again to select the element.

How can we enhance the VoiceOver user experience to achieve the same level of speed and precision found in default behaviour?

Fortunately, SwiftUI Release 3 provides a particular set of tools for managing accessibility focus. It includes the AccessibilityFocusState property wrapper and the accessibilityFocused view modifier. We can handle accessibility focus in a similar way that we manage it without assistive technologies.

https://developer.apple.com/documentation/swiftui/accessibilityfocusstate

struct CheckoutView: View {
@AccessibilityFocusState
private var isNameFieldFocused: Bool

@State private var name = ""

var body: some View {
NavigationView {
Form {
/*
Imagine long list of fields here....
*/
TextField("Full Name", text: $name, prompt: Text("your name"))
.accessibilityFocused($isNameFieldFocused)
}
.onChange(of: isNameFieldFocused) { newValue in
print(newValue)
}
}
}
}

In the example above, the AccessibilityFocusState property wrapper is used to define a variable that represents whenever the name field is focused. SwiftUI initializes the variable with the false value by default because the user can focus on any other screen area. We also use the accessibilityFocused view modifier to bind the focus state of a particular view to a variable holding its value.

Of course, in the real app, you would have multiple fields on the screen. Create an enum for all of them, and use another overload of the accessiblityFocused method and you can gain full control:

enum FocusableField {
case name
case address
}

struct CheckoutView: View {
@AccessibilityFocusState
private var focus: FocusableField?

@State private var name = ""
@State private var address = ""

var body: some View {
NavigationView {
Form {
/*
Imagine long list of fields here....
*/
TextField("Full Name", text: $name, prompt: Text("your name"))
.accessibilityFocused($focus, equals: .name)
TextField("Address", text: $address, prompt: Text("your address"))
.accessibilityFocused($focus, equals: .address)
}
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
focus = .name
}
}
}
}
}

Now, the focus determines which field is focused. It could be that we need to set an initial focus like in the example, or we can use onChange and map any custom logic: bottom sheet close, transitions, status updates, etc.

Proper and strategic focus handling can enhance the overall experience for VoiceOver users, ensuring optimal accessibility for the app. Additionally, it’s crucial to verify that any alterations contribute to improvement without unnecessarily complicating the app. Consulting with users and implementing only essential changes is key.

I hope you enjoyed the article and are inspired to start working on improving accessibility. We encourage you to work according to the official documentation on accessibility in iOS apps. These can be found here:

https://developer.apple.com/documentation/objectivec/nsobject/uiaccessibility

Just Eat Takeaway.com is hiring! Want to come work with us? Apply today.

--

--