3

I have a button that can toggle a label being shown:

class ViewController: UIViewController {
    @IBOutlet weak var label: UILabel!
    @IBOutlet weak var button: UIButton!

    override func viewDidLoad() {
        super.viewDidLoad()
        button.accessibilityLabel = "You can tap this really long string that i'm testing"
        label.accessibilityLabel = "This is a label"
    }

    @IBAction func buttonTapped(_ sender: UIButton) {
        label.isHidden = !label.isHidden
        if !label.isHidden {
            UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, label)
        }
    }
}

When tapping the button, if the label is shown I activate the label to be read by VoiceOver. The problem is VoiceOver automatically starts reading the button's accessibilityLabel when the user taps the button. This results in VoiceOver reading half of the button's accessibilityLabel before swapping to reading the label's accessibilityLabel (e.g. "You can tap this really...This is a label").

Is there a way I can know when VoiceOver is done reading the button's accessibilityLabel and only then call UIAccessibilityPostNotification? Or is there a way to disable the button from being read again by VoiceOver when the user taps the button?

An example project can be seen here: https://github.com/rajohns08/VoiceOverTest

Adam Johns
  • 35,397
  • 25
  • 123
  • 176

3 Answers3

4

You can set the following attribute on the button, and it will no longer read out the button again when it's being clicked:

button.accessibilityTraits += UIAccessibilityTraitStartsMediaSession

This tells the system that the button initiates a multimedia event, and it shouldn't speak anything out when activated.

Reference documentation from Apple: https://developer.apple.com/documentation/uikit/uiaccessibilitytraits/1620173-startsmediasession

Use this trait to silence the audio output of an assistive app, such as VoiceOver, during a media session that you don't want to interrupt. For example, you might use this trait to silence VoiceOver speech while the user is recording audio.

In regards to waiting on elements to finish reading before moving to other elements: I only was able to find out how to wait for Announcements to finish, by subscribing to this notification: .UIAccessibilityAnnouncementDidFinish

That works fine when the system is done reading out announcements dispatched like this:

UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, title)

However, i wasn't able to figure out how to wait on things like LayoutChanged and ScreenChanged to get finished reading. They do not dispatch the above announcement notification. If you can figure that out, please let me know.

Derek Lee
  • 3,452
  • 3
  • 30
  • 39
FranticRock
  • 3,233
  • 1
  • 31
  • 56
  • Some piece of information about the end of the VoiceOver announcement if need be ⟹ https://stackoverflow.com/a/52459406/3825084 – XLE_22 Nov 26 '21 at 14:29
2

Unfortunately, my gut (an eminently citable source) says you cannot, and should not, inspect and work around any speech generated by VoiceOver in response to user navigation or action. The user should not have to wait through the button label before hearing the outcome of activating the button. That said, you might reconsider using such a long button label and include extra information in the accessibilityHint, which is read after a delay, instead.

Justin
  • 20,509
  • 6
  • 47
  • 58
  • ... unless hints aren't activated by the user himself in the settings (Settings-Accessibility-VoiceOver-Verbosity-SpeakHints). – XLE_22 Nov 26 '21 at 14:20
1

One possible approach is to split the content of the accessibilityLabel over a shorter accessibilityLabel and a longer accessibilityHint.

I assume that the reason for the long accessibilityLabel is that there is a need for providing extra information about the button action for users who can't see the screen.

Just like we prefer brief visible button labels so that seeing user can "see fast", voiceover users want to "hear fast", so it's a good idea to keep the accessibilityLabel brief, and have the salient words first, since reading the label is interrupted when the user moves on.

The hint will be read if focus stays long enough on the button.

Users can turn off spoken hints in the settings, so if it is crucial to impart the information every time the button is pressed, then this solution won't work. You would probably have to rely on announcements instead as FranticRock suggests, perhaps combined with a dispatch delay.

It would be interesting to know the use case, maybe that will lead to more ideas!

Pacificana
  • 116
  • 9