Slide 1

Slide 1 text

WHAT’S NEW IN ACCESSIBILITY YUMEMI.SWIFT #12 FEAT. HAKATA.SWIFT 〜WWDC RECAP~

Slide 2

Slide 2 text

⾃⼰紹介 JIERONG LI (李) ▸ 株式会社ゆめみ ▸ iOSエンジニア ▸ KMMでAndroid開発キャッチアップ ▸ 永遠にリリースできず個⼈開発 ▸ https://jierong.dev

Slide 3

Slide 3 text

COVERED SESSIONS ▸ SwiftUI Accessibility: Beyond the basics ▸ Bring accessibility to charts in your app

Slide 4

Slide 4 text

PREVIEW WITHOUT RUNNING APP

Slide 5

Slide 5 text

CUSTOM VIEW IMPLEMENTATION SegmentedControl( selectedSegmentIndex: $selectedSegmentIndex ) { HStack { Image(systemName: "tray.full") Text("Inbox") Spacer() Text("\(tasks.count)") } HStack { Image(systemName: "archivebox") Text("Done") } }

Slide 6

Slide 6 text

CUSTOM VIEW ACCESSIBILITY PREVIEW ▸ Inbox Icon ▸ Label: Inbox Full ▸ Traits: .isImage ▸ Inbox Text ▸ Label: Inbox ▸ Traits: .isStaticText ▸ Count ▸ Label: 5 ▸ Traits: .isStaticText ▸ Done Icon ▸ Label: Archive ▸ Traits: .isImage ▸ Done Text ▸ Label: Done ▸ Traits: .isStaticText

Slide 7

Slide 7 text

CUSTOM VIEW EXPECTED ▸ Inbox ▸ Label: Inbox 5 ▸ Traits: .isButton 
 .isSelected ▸ Done ▸ Label: Done ▸ Traits: .isButton

Slide 8

Slide 8 text

CUSTOM VIEW BELOW IOS 15 SegmentedControl( selectedSegmentIndex: $selectedSegmentIndex ) { HStack { Image(systemName: "tray.full") Text("Inbox") Spacer() Text("\(tasks.count)") } ... }

Slide 9

Slide 9 text

CUSTOM VIEW BELOW IOS 15 SegmentedControl( selectedSegmentIndex: $selectedSegmentIndex ) { HStack { Image(systemName: "tray.full") Text("Inbox") Spacer() Text("\(tasks.count)") } .accessibilityElement(children: .ignore) ... }

Slide 10

Slide 10 text

CUSTOM VIEW BELOW IOS 15 SegmentedControl( selectedSegmentIndex: $selectedSegmentIndex ) { HStack { Image(systemName: "tray.full") Text("Inbox") Spacer() Text("\(tasks.count)") } .accessibilityElement(children: .ignore) .accessibility(label: Text(“\(tasks.count)”)) ... }

Slide 11

Slide 11 text

CUSTOM VIEW BELOW IOS 15 SegmentedControl( selectedSegmentIndex: $selectedSegmentIndex ) { HStack { Image(systemName: "tray.full") Text("Inbox") Spacer() Text("\(tasks.count)") } .accessibilityElement(children: .ignore) .accessibility(label: Text(“\(tasks.count)”)) .accessibility(addTraits: selectedSegmentIndex == 0 ? [.isButton, .isSelected] : [.isButton] ) ... }

Slide 12

Slide 12 text

CUSTOM VIEW IOS 15 AND ABOVE SegmentedControl( selectedSegmentIndex: $selectedSegmentIndex ) { HStack { Image(systemName: "tray.full") Text("Inbox") Spacer() Text("\(tasks.count)") } HStack { Image(systemName: "archivebox") Text("Done") } }

Slide 13

Slide 13 text

CUSTOM VIEW IOS 15 AND ABOVE SegmentedControl( selectedSegmentIndex: $selectedSegmentIndex ) { HStack { Image(systemName: "tray.full") Text("Inbox") Spacer() Text("\(tasks.count)") } HStack { Image(systemName: "archivebox") Text("Done") } } .accessibilityRepresentation { Picker("", selection: $selectedSegmentIndex) { Text("Inbox \(tasks.count)").tag(0) Text("Done").tag(1) } .pickerStyle(.segmented) }

Slide 14

Slide 14 text

EXTERNAL VIEW (CHARTVIEW) BELOW IOS 15 BarChart() .data(dataSet.map(\.value)) .chartStyle(chartStyle) 🤷

Slide 15

Slide 15 text

EXTERNAL VIEW (CHARTVIEW) IOS 15 AND ABOVE BarChart() .data(dataSet.map(\.value)) .chartStyle(chartStyle)

Slide 16

Slide 16 text

EXTERNAL VIEW (CHARTVIEW) IOS 15 AND ABOVE BarChart() .data(dataSet.map(\.value)) .chartStyle(chartStyle) .accessibilityChildren { HStack { ForEach(dataSet) { data in Rectangle() .accessibilityLabel(data.label) .accessibilityValue("\(data.value)") } } }

Slide 17

Slide 17 text

ROTOR BELOW IOS 15 UIKit Only

Slide 18

Slide 18 text

ROTOR IOS 15 AND ABOVE VStack { ForEach(lines) { line in Text(line.content) } } .accessibilityRotor("Titles") { ForEach(lines) { line in if line.isTitle { AccessibilityRotorEntry( line.content, id: line.id ) } } }

Slide 19

Slide 19 text

ROTOR IOS 15 AND ABOVE VStack { ForEach(lines) { line in Text(line.content) } } .accessibilityRotor(.headings) { ForEach(lines) { line in if line.isTitle { AccessibilityRotorEntry( line.content, id: line.id ) } } }

Slide 20

Slide 20 text

CONTROL FOCUS @ACCESSIBILITYFOCUSSTATE @AccessibilityFocusState var isFocused: Bool

Slide 21

Slide 21 text

CONTROL FOCUS @ACCESSIBILITYFOCUSSTATE struct NotificationBanner: View { @Binding var notification: Notification? @State var timer: Timer? @AccessibilityFocusState var isNotificationFocused: Bool var body: some View { content .accessibilityFocused(isNotificationFocused) } func startTimer() { timer = Timer.scheduledTimer( withTimeInterval: 3, repeats: true) { _ in if !isNotificationFocused { notification = nil } } } }

Slide 22

Slide 22 text

AUDIO GRAPH AXCHART protocol AXChart: NSObjectProtocol { var accessibilityChartDescriptor: AXChartDescriptor? { get set } }

Slide 23

Slide 23 text

AUDIO GRAPH AXCHART protocol AXChart: NSObjectProtocol { var accessibilityChartDescriptor: AXChartDescriptor? { get set } } class AXChartDescription: NSObject { convenience init( title: String? = nil, summary: String? = nil, xAxis: AXDataAxisDescriptor, yAxis: AXNumericDataAxisDescriptor? = nil, additionalAxes: [AXDataAxisDescriptor] = [], series: [AXDataSeriesDescriptor] ) ... }

Slide 24

Slide 24 text

ご清聴ありがとうございました