I've got a page where there is a text field for the user to enter a value. If they enter an incorrect value it shows an error message (text and a systemImage, not an alert) and when they enter a correct value it shows a continue button.
Once the user has entered anything and dismissed the keyboard I want voiceover to automatically focus on either the error message or the continue button.
The only way I've sort of been able to make this work is by using accessibilitySortPriority with a ternary conditional that says if the field is not empty and the keyboard is not focused then make this the highest sort priority.
Sure, that works (kind of) because it will in fact focus on the correct element once the keyboard is dismissed, but if the user changes their entry in the text field, then dismisses the keyboard, it stays on the text field instead of focusing once again on the error message or continue button.
Also, changing the sort priority of the entire page is a bit problematic because everything is out of order if the user needs to swipe to move around the page elements properly.
I've also looked into AccessibilityFocusState because the docs say that the element will gain VO focus when the bool is true and when you move away from that element the bool becomes false. However, nothing I've tried with this seems to do anything at all.
What I want to have happen is as follows:
- User loads the page
- VO reads the elements from top to bottom
- User taps to enter a value in the text field
- User dismisses the keyboard
- Either the error msg or continue button appears on screen and VO immediately focuses on it
- User taps to change their entry in the text field
- User dismisses the keyboard
- Either the error msg or continue button appears on screen and VO immediately focuses on it
If anyone is interested, the answer is AccessibilityFocusState, but you need to use it in a very specific way.
- Create an enum with the various focus choices on your page
- Create @AccessibilityFocusState private var accessFocus: EnumName?
- Use .accessibilityFocused($accessFocus, equals: .yourEnumCase) as a modifier to a view to which you wish to focus
- Your actions can now change the value of accessFocus to the field in which you want voiceover to focus by:
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
accessFocus = .yourEnumCase
}