Accessibility ensures your app is usable by everyone. Learn to implement accessibility features in iOS apps.
Accessibility Labels
Basic Labels
swiftimport SwiftUI struct AccessibilityView: View { var body: some View { VStack { // Good accessibility Image(systemName: "star.fill") .accessibilityLabel("Favorite") .accessibilityHint("Mark as favorite") // Bad accessibility Image(systemName: "star.fill") } } }
Custom Accessibility Labels
swiftstruct CustomAccessibilityView: View { @State private var isOn = false var body: some View { Toggle("Dark Mode", isOn: $isOn) .accessibilityLabel("Enable dark mode") .accessibilityHint("Changes app appearance to dark theme") } }
VoiceOver Support
Screen Reader Support
swiftstruct VoiceOverView: View { @State private var selectedRating = 3 var body: some View { VStack { // Custom accessibility for rating HStack { ForEach(1...5, id: \.self) { rating in Image(systemName: rating <= selectedRating ? "star.fill" : "star") .accessibilityLabel(rating <= selectedRating ? "Selected" : "Unselected") .accessibilityAddTraits(rating <= selectedRating ? [.isSelected] : []) .onTapGesture { selectedRating = rating } } } .accessibilityLabel("Rating") .accessibilityValue("\(selectedRating) out of 5") } } }
Accessibility Actions
swiftstruct AccessibilityActionsView: View { @State private var isFavorite = false var body: some View { Image(systemName: "heart") .foregroundColor(isFavorite ? .red : .gray) .accessibilityLabel(isFavorite ? "Remove from favorites" : "Add to favorites") .accessibilityAction(.default) { isFavorite.toggle() } .accessibilityAction(.magicTap) { // Double-tap with two fingers isFavorite.toggle() } } }
Dynamic Type
Support Text Scaling
swiftstruct DynamicTypeView: View { var body: some View { VStack(alignment: .leading, spacing: 16) { Text("Headline") .font(.headline) .dynamicTypeSize(...DynamicTypeSize.accessibility3) Text("Body") .font(.body) .dynamicTypeSize(...DynamicTypeSize.accessibility3) Text("Caption") .font(.caption) .dynamicTypeSize(...DynamicTypeSize.accessibility3) } .padding() } }
Scalable Layouts
swiftstruct ScalableView: View { @Environment(\.dynamicTypeSize) var dynamicTypeSize var body: some View { ScrollView { VStack(alignment: .leading, spacing: dynamicTypeSize == .accessibility1 ? 24 : 16) { Text("Dynamic Type Support") .font(.title) .fontWeight(.bold) Text("This text scales with your device settings") .font(.body) } .padding() } } }
Accessibility Elements
Accessible Images
swiftstruct AccessibleImageView: View { var body: some View { // Decorative image Image(systemName: "star.fill") .accessibilityHidden(true) // Meaningful image Image(systemName: "person.crop.circle.fill") .accessibilityLabel("Profile picture") // Image with description Image(systemName: "photo") .accessibilityLabel("Photo of a sunset") .accessibilityHint("Tap to view full size") } }
Accessible Groups
swiftstruct AccessibilityGroupsView: View { let user = User(name: "John Doe", email: "john@example.com") var body: some View { HStack { Circle() .fill(Color.blue) .frame(width: 50, height: 50) VStack(alignment: .leading) { Text(user.name) .font(.headline) Text(user.email) .font(.caption) .foregroundColor(.secondary) } } .accessibilityElement(children: .combine) .accessibilityLabel("User: \(user.name), \(user.email)") } } struct User { let name: String let email: String }
Semantic UI
Semantic Views
swiftstruct SemanticView: View { var body: some View { VStack { // Header Text("Welcome") .font(.title) .accessibilityAddTraits(.isHeader) // Button Button("Get Started") { // Action } .accessibilityHint("Starts the onboarding process") // Link Text("Learn more") .accessibilityAddTraits(.isLink) .onTapGesture { // Navigation } } } }
Accessibility Traits
Common Traits
swiftstruct AccessibilityTraitsView: View { @State private var isSelected = false var body: some View { VStack { // Button trait Button("Tap me") {} .accessibilityAddTraits(.isButton) // Selected state Image(systemName: isSelected ? "checkmark.circle.fill" : "circle") .accessibilityAddTraits(isSelected ? [.isSelected] : []) // Static text Text("Information") .accessibilityAddTraits(.isStaticText) // Header Text("Section Title") .accessibilityAddTraits(.isHeader) } } }
Accessibility Identifier
UI Testing Support
swiftstruct TestableView: View { var body: some View { VStack { TextField("Username", text: $username) .accessibilityIdentifier("usernameField") SecureField("Password", text: $password) .accessibilityIdentifier("passwordField") Button("Login") { // Login action } .accessibilityIdentifier("loginButton") } } }
Reduced Motion
Support Motion Preferences
swiftstruct ReducedMotionView: View { @Environment(\.accessibilityReduceMotion) var reduceMotion @State private var isAnimating = false var body: some View { Circle() .fill(Color.blue) .frame(width: 100, height: 100) .scaleEffect(isAnimating ? 1.5 : 1.0) .animation(reduceMotion ? nil : .spring(), value: isAnimating) .onTapGesture { isAnimating.toggle() } } }
High Contrast
Support Dark Mode
swiftstruct HighContrastView: View { @Environment(\.accessibilityReduceTransparency) var reduceTransparency @Environment(\.colorScheme) var colorScheme var body: some View { ZStack { if reduceTransparency { Color.black } else { Color.black.opacity(0.8) } Text("High Contrast Content") .foregroundColor(.white) .padding() } .cornerRadius(reduceTransparency ? 0 : 20) } }
Best Practices
- 1Labels: Provide clear, descriptive labels
- 2Traits: Use appropriate accessibility traits
- 3Focus: Manage accessibility focus
- 4Testing: Test with VoiceOver
- 5Feedback: Provide meaningful feedback
- 6Language: Support right-to-left languages
- 7Testing: Use Accessibility Inspector
- 8Users: Test with real accessibility users
Accessibility makes your app usable by everyone!