Transcript
In this demo, you’ll take a quick look at Xcode’s Accessibility Inspector and learn a few more VoiceOver tricks. If you’re following along, start up Xcode and open the WaitForIt app in the Starter folder.
Now, select an iOS simulator as Run Destination.
In ContentView, load its preview, make sure you’re in Live mode, then tap Fetch a joke.
ZStack {
Text(jokeService.joke)
.multilineTextAlignment(.center)
.padding(.horizontal)
VStack {
Spacer()
Button(action: {
Task {
try? await jokeService.fetchJoke()
}
}) {
Text("Fetch a joke")
.padding(.bottom)
.opacity(jokeService.isFetching ? 0 : 1)
.overlay {
if jokeService.isFetching { ProgressView() }
}
}
}
}
The user interface is very simple, only some text and a button.
Chuck Norris joke.
Tapping the button sends a request to an API that returns a randomThe query item specifies the dev category, so all the jokes have a techie flavor. Warning: Some of these jokes are a little violent.
ZStack
, VStack
, Text
, Button
, and the button’s Text
label. Yet, VoiceOver will read out both text values because SwiftUI generates accessibility elements.
To try this out, switch to Selectable mode.
ZStack
in the code editor,
then show the Accessibility Inspector.
If it says No Selection, select another inspector, like Attributes, then click back to Accessibility.
- Label defaults to the element’s label Joke appears here or Fetch a joke.
- Value is none because neither element has a value.
- Traits defaults to isStaticText or .isButton.
-
Button
.
Actions defaults to activate for the
Now, to find out how this sounds on your device, you’ll connect your iOS device to your Mac. First, change the target’s Bundle Identifier, and set a Team.
If necessary, adjust the project’s iOS Deployment Target to match your device.
If you haven’t used this device as a run destination before, turn on Developer mode:
You’ll see an alert warning you that Developer Mode reduces the security of your device. Tap the alert’s Restart button.
After your device restarts and you unlock it, you’ll see an alert asking you to confirm that you want to turn on Developer Mode.
Tap Turn On to acknowledge the reduction in security protection in exchange for allowing Xcode and other tools to execute code, then enter your device passcode when prompted.
Now, connect your device to your Mac with a cable. Use an Apple cable, as other-brand cables might not work for this purpose.
Select your device from the run destination menu: It appears near the top, above the simulators
Then build and run the app on your device:
Turn on VoiceOver.
Swipe up with two fingers to hear: “Wait for it. Joke appears here. Fetch a joke. Button.”
With the button selected, double-tap to activate it.
When the joke appears, swipe up with two fingers to hear VoiceOver read the joke, then return to the button.
To really test whether a VoiceOver user can use your app, triple-tap with three fingers to turn on the screen curtain:
This turns off the display while keeping the screen contents active. VoiceOver users can use this for privacy or if the screen light would disturb other people, like in a dark theater.
Double-tap anywhere on the screen to activate the button, wait a bit, then swipe up with two fingers to hear the joke and return to the button. You can’t see the joke and button, so you must rely entirely on VoiceOver information and gestures.
Triple-tap with three fingers to turn off the screen curtain and show the display again.
Back in Xcode, open RefreshableView.
Change the run destination to a simulator and refresh the preview.
This also fetches a joke, but there’s no button. This view fetches a joke when it loads, and the user can pull down to refresh the view, which fetches a new joke.
List {
Text("Pull to refresh")
.font(.largeTitle)
.listRowSeparator(.hidden)
Text(jokeService.joke)
.multilineTextAlignment(.center)
.lineLimit(nil)
.lineSpacing(5.0)
.padding()
.font(.title)
}
List
in the code editor.
Text
views has a Label and Traits.
refreshable
modifier works with List
, not with a stack, and List
isn’t a “real” SwiftUI element, so the Accessibility Inspector can’t really parse it. What happens if you try to use VoiceOver with this view?
ContentView()
and uncomment RefreshableView()
:
Change run destination to your device, then build and run.
A joke loads right away. Swipe up with two fingers:
VoiceOver says: “Wait for it. Pull to refresh.” and the joke. But now what? Swiping down doesn’t register as a pull-down action.
VoiceOver lets you use a standard gesture: Double-tap and hold your finger on the screen until you hear three rising tones, then make the gesture.
VoiceOver gestures resume when you lift your finger after making the standard gesture.
WaitForIt is a very simple app, and the default SwiftUI accessibility is sufficient. Most apps are much more complex. In the next lesson, you’ll learn what you can do if the default accessibility isn’t enough.