Skip to content

Spec: Text ‐ Accessibility

Matt Carroll edited this page Feb 24, 2024 · 1 revision

Accessibility

Swift's Text contents are read out loud by default by VoiceOver. The Text offers various methods for configuring VoiceOver and providing accessibility information.

VoiceOver Basics

If you are unfamiliar with VoiceOver, here is a very brief guide on VoiceOver Basics to make sure you know how to test our example application.

It's recommended to use a real physical device for accessibility testing, but as a fallback option, the Accessibility Inspector is also available (this won't be discussed in this spec).

Turn on VoiceOver

To turn on VoiceOver, go to Settings > Accessibility > VoiceOver, then toggle the switch for the VoiceOver. Now, your phone is in "VoiceOver mode" and the screen's content will be read out loud.

VoiceOver changes the gestures you use to control your phone, the gestures you are familiar with will no longer work! When VoiceOver is on, you need to use VoiceOver gestures to operate iPhone, so to prevent any headache (e.g. not being able to turn off VoiceOver after your accessibility testing is complete), make sure you familiarize yourself with VoiceOver gestures in the next section before you toggle VoiceOver.

VoiceOver gestures

Here, I share the VoiceOver gestures that you'll probably need to use to test our example app's accessibility.

  • jump to next and previous items: swipe right and left
  • select and speak item: single tap
  • activate item, e.g. "click" on a button to jump to the next screen, or open an application: double tap
  • access rotor: rotate two fingers (as if you’re turning a dial)
    • the rotor will show up, and you can switch between the rotor's options by rotating clockwise or counterclockwise. If you are happy with the selection (e.g. headings for testing the accessibilityHeading below), lift up your fingers.
  • jump to next or previous item depending on the rotor settings (e.g. jump to next heading): swipe up and down
  • scroll up or down one page: three finger swipe up or down
  • go to home: drag one finger up from the bottom edge of the screen until you hear two rising tones
  • open app switcher: drag one finger up from the bottom edge of the screen until you hear three rising tones

Please reference the official guide on VoiceOver gestures and the guide on how to operate your phone (unlock, go to home, switch apps) in case you get stuck.

speechAdjustedPitch

The pitch of the spoken text can be raised and lowered with the speechAdjustedPitch instance method. The instance method receives a Double value indicating how much higher or lower to change the pitch.

Values between -1 and 0 result in a lower pitch while values between 0 and 1 result in a higher pitch. The method clamps values to the range -1 to 1.

Text("Lowered pitch").speechAdjustedPitch(-1.0)
Text("Slightly lowered pitch").speechAdjustedPitch(-0.3)
Text("Normal pitch").speechAdjustedPitch(0)
Text("Slightly raised pitch").speechAdjustedPitch(0.3)
Text("Raised pitch").speechAdjustedPitch(1.0)

speechAlwaysIncludesPunctuation

The speechAlwaysIncludesPunctuation "sets whether VoiceOver should always speak all punctuation in the text view".

When enabled, commas, apostrophes, etc. are announced by VoiceOver.

// Apostrophes, commas, semicolon, etc are all announced.
Text("Includes punctuation: All the world's a stage, And all the men and women merely players;").speechAlwaysIncludesPunctuation(true)

// The text is simply spoken without punctuation.
Text("Does not include punctuation: All the world's a stage, And all the men and women merely players;").speechAlwaysIncludesPunctuation(false)

speechSpellsOutCharacters

When speechSpellsOutCharacters is enabled, the text is spoken as individual letters, character by character.

This is important for text that is not meant to be spoken together, like:

  • An acronym that isn’t a word, like APPL, spoken as “A-P-P-L”.
  • A number representing a series of digits, like 25, spoken as “two-five” rather than “twenty-five”.
Text("APPL").speechSpellsOutCharacters(true)
Text("125").speechSpellsOutCharacters(true)

accessibilityHeading

accessibilityHeading adds heading level information to the text. The levels go from h1 to h6, and there is an additional unspecified level.

Assistive technologies can use this to improve a users navigation through multiple headings.

Users can use the rotor to select "Headings", then they can jump from one heading to the next, this enables users finding the content they are looking for more easily.

Text("Heading h1").accessibilityHeading(.h1).accessibilityAddTraits([.isHeader])

For the headings to be recognized, I also had to add the accessibilityAddTraits modifier with the .isHeader value.

Even though I expected, I didn't find a way to jump only between headers of the same level, and VoiceOver simply jumps from one heading to the next regardless of their level.

accessibilityLabel

When VoiceOver is supposed to announce an alternative instead of the Text's content, you can use the accessibilityLabel. It accepts simple strings and localized strings, too.

Text("Text with accessibility label").accessibilityLabel("If the text has an accessibility label, only the accessibility label is spoken by VoiceOver, the text itself is skipped.")

Text("Not announced").accessibilityLabel("Announced")

Missing

There are, unfortunately, two modifiers that I couldn't understand how they work, or better put, I couldn't create a demo that shows the modifiers in action:

  • speechAnnouncementsQueued : this should control "whether to queue pending announcements behind existing speech rather than interrupting speech in progress". In all examples I created, the queuing never worked and the text was immediately announced as the text was selected.
  • accessibilityTextContentType : assistive technologies are supposed to "use this property to choose an appropriate way to output the text". Unfortunately, regardless of the accessibility text content type, the tests were always announced the same.