2021 SwiftUI & UI FrameworksAccessibility & Inclusion
WWDC21 · 23 min · SwiftUI & UI Frameworks / Accessibility & Inclusion
Create accessible experiences for watchOS
Discover how you can build a top-notch accessibility experience for watchOS when you support features like larger text sizes, VoiceOver, and AssistiveTouch. We’ll take you through adding visual and motor accessibility support to a SwiftUI app built for watchOS, including best practices around API integration, experience, and more.
Watch at developer.apple.com ↗Code shown on screen · 10 snippets
Dynamic Type for PlantView
struct PlantView: View {
var plant: Plant
var body: some View {
VStack(alignment: .leading) {
Text(plant.name)
.font(.title3)
HStack() {
PlantImage(plant: plant)
PlantTaskList(plant: $plant)
}
PlantTaskButtons(plant: $plant)
}
}
} Line limits for PlantTaskLabel
struct PlantTaskLabel: View {
let task: PlantTask
var plant: Plant
var body: some View {
HStack {
Image(systemName: task.systemImageName)
.imageScale(.small)
Text(plant.stringForTask(task: task))
}
.lineLimit(3)
.font(.caption2)
}
} Alternate layouts for PlantContainerView
struct PlantContainerView: View {
(\.sizeCategory) var sizeCategory
var plant: Plant
var body: some View {
if sizeCategory < .extraExtraLarge {
PlantViewHorizontal(plant: $plant)
} else {
PlantViewVertical(plant: $plant)
}
}
} Element grouping for PlantCellView
struct PlantCellView: View {
var plantData: PlantData
var plant: Plant
var plantIndex: Int {
plantData.plants.firstIndex(where: { $0.id == plant.id })!
}
var body: some View {
NavigationLink(destination: PlantEditView(plant: plant).environmentObject(plantData)) {
PlantContainerView(plant: $plantData.plants[plantIndex])
.padding()
}
}
} Accessibility labels for PlantTaskLabel
struct PlantTaskLabel: View {
let task: PlantTask
var plant: Plant
var body: some View {
HStack {
Image(systemName: task.systemImageName)
.imageScale(.small)
Text(plant.stringForTask(task: task))
.accessibilityLabel(plant.accessibilityStringForTask(task: task))
}
.lineLimit(3)
.font(.caption2)
}
} Accessibility labels for PlantButton
struct PlantButton: View {
let task: PlantTask
let action: () -> Void
private var isTapped: Bool = false
var body: some View {
Button(action: {
self.isTapped.toggle()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.isTapped.toggle()
}
action()
}) {
Image(systemName: task.systemImageFillName)
.foregroundColor(task.color)
.scaleEffect(isTapped ? 1.5 : 1)
.animation(nil, value: 0)
.rotationEffect(.degrees(isTapped ? 360 : 0))
.animation(.spring(), value: 0)
.imageScale(.large)
}
.buttonStyle(BorderedButtonStyle())
.accessibilityLabel("Log \(task.name)")
}
} Custom control accessibility for PlantTaskFrequency
struct PlantTaskFrequency: View {
let task: PlantTask
var plant: Plant
let increment: () -> Void
let decrement: () -> Void
var value: Int {
switch task {
case .water:
return plant.wateringFrequency
case .fertilize:
return plant.fertilizingFrequency
default:
return 0
}
}
var body: some View {
Section(header: Text("\(task.name) frequency in days"), content: {
CustomCounter(value: value, increment: increment, decrement: decrement)
.accessibilityElement()
.accessibilityAdjustableAction { direction in
switch direction {
case .increment:
increment()
case .decrement:
decrement()
default:
break
}
}
.accessibilityLabel("\(task.name) frequency")
.accessibilityValue("\(value) days")
})
}
} Make static element focusable
struct FreeDrinkView: View {
var didCancel = false
var didAccept = false
var showDetail = false
var body: some View {
VStack(spacing:10) {
FreeDrinkTitleView()
FreeDrinkInfoView()
.accessibilityRespondsToUserInteraction(true)
HStack {
CancelButton(buttonTapped: $didCancel)
AcceptButton(buttonTapped: $didAccept)
}
}
.onTapGesture {
showDetail.toggle()
}
.sheet(isPresented: $showDetail, onDismiss: dismiss) {
DrinkDetailModalView()
}
}
} AssistiveTouch cursor frame
struct DrinkView: View {
var currentDrink:DrinkInfo
var body: some View {
HStack(alignment: .firstTextBaseline) {
DrinkInfoView(drink:currentDrink)
Spacer()
NavigationLink(destination: EditView()) {
Image(systemName: "ellipsis")
.symbolVariant(.circle)
}
.contentShape(Circle().scale(1.5))
}
}
} AssistiveTouch Action Menu
PlantContainerView(plant: plant)
.padding()
.accessibilityElement(children: .combine)
.accessibilityAction {
// Edit action
} label: {
Label("Edit", systemImage: "ellipsis.circle")
} Resources
Related sessions
-
28 min -
16 min -
38 min