Accessibility in SwiftUI
Accessibility in app development is all about making sure everyone, regardless of their abilities, can use your app. Apple’s SwiftUI framework makes integrating accessibility features more streamlined. Here, we'll explore key strategies to make your SwiftUI apps more inclusive.
Understanding Accessibility
Accessibility isn't just a checklist; it’s a mindset. Think of it as designing for the broadest range of human abilities and environments. It's about empathy, respecting your users, and enabling them to interact with your app seamlessly.
Accessibility in SwiftUI: Getting Started
SwiftUI simplifies the process with built-in accessibility modifiers.
Let’s cover some of the fundamental elements.
1. Labeling and Identifying Elements
The .accessibility(label:) modifier help users with assistive technologies understand your UI.
Text("Play")
.accessibilityLabel(label: Text("Play button"))
.accessibilityIdentifier("play_button")
.accessibilityIdentifier(_:) is useful for UI testing.
2. Providing Hints
Hints can help users understand the purpose of interactive elements.
The .accessibilityHint(_:) modifier provides additional information.
Button(action: {
// Action
}) {
Image(systemName: "trash")
}
.accessibilityHint(Text("Deletes the current item"))
3. Grouping Elements
Use the .accessibilityElement(children:) modifier to logically group UI elements together, improving navigation.
VStack {
Text("Name")
TextField("Enter your name", text: $name)
}
.accessibilityElement(children: .combine)
4. Hide non-essential Elements
Use the .accessibilityHidden(_:) modifier to hide non-essential elements from assistive technologies.
Text("I am here only for the fancy design!")
.accessibilityHidden(true)
5. Accessibility speach language
Use the .accessibilityLanguage modifier to set the language of the accessibility label.
let attributedString = AttributedString(
"Nederland is bekend om zijn tulpen en windmolens.",
attributes: AttributeContainer([
.accessibilitySpeechLanguage: "nl_NL"
])
)
Text(attributedString)
6. Accessibility Actions
You can create custom actions that VoiceOver users can trigger using the .accessibilityAction(named:_:) modifier.
Image(systemName: "heart")
.accessibilityAction(named: Text("Like song")) {
// Handle action
}
7. Accesibility Announcements
Use AccessibilityNotification to provide custom announcements for VoiceOver users.
@State
private var isLoading = false
var body: some View {
VStack {
Button("Search catalog") {
isLoading = true
}
if isLoading {
ProgressView()
}
}
// Track state changes
.onChange(of: isLoading) { _, isLoading in
if isLoading {
UIAccessibility.post(
notification: .announcement,
argument: "Search in progress"
)
}
}
}
8. Accessibility focus state
Use the @AccessibilityFocusState and .accessibilityFocused(_:) modifier to manage the focus state of an element.
@AccessibilityFocusState
private var isEmailFocused: Bool
@State
private var email = ""
var body: some View {
Form {
TextField("Email", text: $email, prompt: Text("Email"))
// Binds the focus state of this TextField to the 'isEmailFocused' property.
.accessibilityFocused($isEmailFocused)
// The border color changes based on whether the TextField is focused:
.border(isEmailFocused ? .red : .clear)
}
}
Sample taken from: https://appt.org/en/docs/swiftui/samples/accessibility-focus-indicator
9. Accessibility sort priority
Use the accessibilitySortPriority(_:) modifier to set the sort priority of an accessibility element.
VStack {
Text("First Element")
.accessibilitySortPriority(2) // Reads second
Text("Second Element")
.accessibilitySortPriority(3) // Reads first
Text("Third Element")
.accessibilitySortPriority(1) // Reads third
}
.accessibilityElement(children: .contain) // Groups the stack's children
10. Accessibility traits
Use the accessibilityAddTraits(_:) modifier to set the traits of an accessibility element.
// Button Trait
Text("Tap Me")
.accessibilityAddTraits(.isButton)
// Header Trait
Text("Section Header")
.font(.headline)
.accessibilityAddTraits(.isHeader)
// Link Trait
Text("Visit Website")
.foregroundColor(.blue)
.underline()
.accessibilityAddTraits(.isLink)
// Image Trait
Image(systemName: "star.fill")
.accessibilityAddTraits(.isImage)
// Custom Checkbox Trait
@State
private var isSelected: Bool = false
var body: some View {
CustomCheckbox(isSelected: $isSelected)
.accessibilityAddTraits(isSelected ? [.isSelected] : [])
}
11. Accessibility value
Use the accessibilityValue(_:) modifier to set the value of an accessibility element.
@State
private var progress: Double = 0
var body: some View {
MySlider(value: $progress)
.accessibilityValue("\(progress)")
}
12. Dynamic Type
Support Dynamic Type by using the font modifier with the .largeTitle, .title, .headline, .body, or .caption text styles.
Text("Hello, World!")
.font(.largeTitle)
14. Autocomplete
Autocompletion is an accessibility feature that helps users complete text input.
Use the .searchSuggestions(_:) modifier to provide search suggestions.
ProductList()
.searchable(text: $text)
// Provide search suggestions
.searchSuggestions {
Text("🍎").searchCompletion("apple")
Text("🍐").searchCompletion("pear")
Text("🍌").searchCompletion("banana")
}
15. Dark Mode
Dark mode is an accessibility feature that helps users with light sensitivity or visual impairments.
@Environment(\.colorScheme)
var colorScheme
var body: some View {
HStack {
if colorScheme == .dark {
// Show moon icon (dark mode)
Image(systemName: "moon.fill")
} else {
// Show sun icon (light mode)
Image(systemName: "sun.max.fill")
}
Text(colorScheme == .dark ? "In dark mode" : "In light mode")
}
}
16. Sensory Feedback
Sensory feedback, like haptic feedback, can help users with visual impairments navigate your app.
This can be achived using the UIImpactFeedbackGenerator and UINotificationFeedbackGenerator classes.
Button("Tap Me for Haptic Feedback") {
UIImpactFeedbackGenerator(style: .heavy)
.impactOccurred()
// Action
}
Button("Tap Me for Success") {
UINotificationFeedbackGenerator()
.notificationOccurred(.success)
// Action
}
Testing Accessibility
Regularly test your app's accessibility using tools like VoiceOver.
Navigate through your app and ensure that every element makes sense and that there are no barriers to interaction.
Conclusion
Accessibility is integral to creating inclusive, user-friendly apps. With SwiftUI's robust accessibility features, there's no excuse for not making your app usable by everyone. It's about designing with empathy and making sure your app stands out by being accessible to all.
Tip:
Use the Accessibility Inspector in Xcode to check your app's accessibility, and enable and disable accessibility features to see how your app behaves.
Resources:
- Accessibility modifiers
- Apple's Accessibility Documentation
- Human Interface Guidelines: Accessibility
Read more
- Simplifying multiplatform Colors • 15 minutes reading time.
- Why You Should Avoid Using AnyView in SwiftUI • 4 minutes reading time.
- LabeledContent in SwiftUI • 2 minutes reading time.
Share
Share Mastodon Twitter LinkedIn Facebook