Dunfey · Hotel WWDC as data, est. 1983
Front desk everything
Years
Topics

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 ↗

Transcript all transcripts

Code shown on screen · 10 snippets

Dynamic Type for PlantView swift · at 4:48 ↗
struct PlantView: View {
    @Binding 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 swift · at 5:00 ↗
struct PlantTaskLabel: View {
    let task: PlantTask
    @Binding 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 swift · at 5:48 ↗
struct PlantContainerView: View {
    @Environment(\.sizeCategory) var sizeCategory
    @Binding var plant: Plant
    
    var body: some View {
        if sizeCategory < .extraExtraLarge {
            PlantViewHorizontal(plant: $plant)
        } else {
            PlantViewVertical(plant: $plant)
        }
    }
}
Element grouping for PlantCellView swift · at 8:56 ↗
struct PlantCellView: View {
    @EnvironmentObject 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 swift · at 9:38 ↗
struct PlantTaskLabel: View {
    let task: PlantTask
    @Binding 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 swift · at 10:03 ↗
struct PlantButton: View {
    let task: PlantTask
    let action: () -> Void
    @State 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 swift · at 11:07 ↗
struct PlantTaskFrequency: View {
    let task: PlantTask
    @Binding 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 swift · at 19:50 ↗
struct FreeDrinkView: View {
    @State var didCancel = false
    @State var didAccept = false
    @State 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 swift · at 21:12 ↗
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 swift · at 22:48 ↗
PlantContainerView(plant: plant)
    .padding()
    .accessibilityElement(children: .combine)
    .accessibilityAction {
        // Edit action
    } label: {
        Label("Edit", systemImage: "ellipsis.circle")
    }

Resources