UIAccessibility - Set focus on previous accessibility element when dismissing a modal view

I am implementing accessibility into our current project and have some issues on setting focus on an element in a previous view.


An example would be:

A user selects on a tableview cell at index 3. In

didSelectRowAt
, we present to the user a
UIActionSheet
where the user makes a selection. Once the user makes a selection and the presented
UIActionSheet
is dismissed, the accessibility focus (SHOULD?) be selected at tableview cell index 3, but instead the focus is set back to the initial element in the view, in my case most top-left element.


I have used

UIAccessibility.Notification
.screenChanged
and
UIView.accessibilityViewIsModal
to set a new focus when presenting a modal view, but this doesn't seem to have a good solution to point back to a previous element when dismissing a view.


Any insight on how to track the previous accessibility element focus would be greatly appreciated.

Replies

Im here because of the same problem. TL;DR; The Poor UX workaround is to delay 1s or more inside your dismiss handler


UIAlertAction(title: Localizer.Cancel, style: .cancel, handler: {
     DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) {
          UIAccessibility.post(notification: .screenChanged, argument: accessibleView)
     }
})


Setup:


Im on iOS 12.2, Swift 5.0, iPad Air 2.


Background:
For now Im using a UIAlertController, and trying to set the VoiceOver focus to an element after the modal is dismissed. T


Problem:

The system is triggering its own '.screenChanged' event, I know this because I can hear the beep associated with a .screenChanged. Usually it focuses on the element that was underneath the modal and closest to the focus ring when the modal was presented.


If I trigger my own .screenChanged notification inside the UIAlertAction handler and set the argument to the UIView I want it only works 1/5 times. The 1/5 times is usually if I open and cancel the Modal quickly. If i leave the focus on the modal for a longer period of time it always goes to the "wrong" element closest to the focus ring but underneath the modal after dismiss.


Attempts:


Ive tried several ways of triggering

UIAccessibility.post(notification: .screenChanged, argument: Any?)


Inside my UIAlertAction handler, with and without delays. The only way I can get it to somewhat work is with a 1 second delay. I dont like this solution for the inevitable race conditions and the poor user experience. Its bad UX because its enough time for the voice over focus to goto the element the system chooses and then to the element I want.


UIAlertAction(title: Localizer.Cancel, style: .cancel, handler: {
     // handler code 
})


At first I thought maybe my handler was being called before the dismiss animation was complete and then the system .screenChanged call was coming afterwards and overriding. I know this is not the case becuase if I slow down the animation speed:


UIApplication.shared.windows.first?.layer.speed = 0.1


you can observe the handler getting called *after* the dismiss animation is completely finished.


Ideas ?

If anyone has any other ideas id love to hear from you, I can test and report back.


Thanks

  • Has either of you found a solution to this problem?

  • You can use UIView.becomeFirstResponder() to trigger a fast VoiceOver focus update. But to achieve that you will need to refactor a lot of your code, which may not actually worth it. Also keep in mind that the VoiceOver behavior for first responder updates is different (or say, strange) on Mac Catalyst.

Add a Comment