Session概要
カスタムローターを統合し、あなたのアプリ内での複雑な状況でユーザがVoiceOverを使いナビゲーションを受けられるようになる方法をお見せします。複雑に入り組んだインタフェースであっても、カスタムローターがあればユーザは探りながら進むことができ、VoiceOver頼りの人に対してもローターでナビゲーションを改善できます。
https://developer.apple.com/videos/play/wwdc2020/10116/
VoiceOverローターとは
- VoiceOver の音量や話す速さを変更したり、画面上で項目間を移動したりすることができ、その他の操作も可能
- ダイヤルを回すように、画面上で指を回転するとローターが起動する
- 上下にスワイプすることで、前の項目や次の項目に移動可能
- 複雑な操作を簡易にすることができる
- カスタムローター
- VoiceOverローターの操作をカスタマイズできる機能
アプリにVoiceOverローターを追加する方法
mapView.accessibilityCustomRotors = [customRotor(for: .stores), customRotor(for: .parks)]
// Custom map rotors
func customRotor(for poiType: POI) -> UIAccessibilityCustomRotor {
UIAccessibilityCustomRotor(name: poiType.rotorName) { [unowned self] predicate in
let currentElement = predicate.currentItem.targetElement as? MKAnnotationView
let annotations = self.annotationViews(for: poiType)
let currentIndex = annotations.firstIndex { $0 == currentElement }
let targetIndex: Int
// 上下のフリップ
switch predicate.searchDirection {
case .previous:
targetIndex = (currentIndex ?? 1) - 1
case .next:
targetIndex = (currentIndex ?? -1) + 1
}
guard 0..<annotations.count ~= targetIndex else { return nil } // Reached boundary
return UIAccessibilityCustomRotorItemResult(targetElement: annotations[targetIndex],
targetRange: nil)
}
}
カスタムローターをテキストで使う方法
- テキストを自動で読み上げるラインローターを活用する
- カスタムローターを利用することで、重要な部分に絞って情報を聞くことができる
- 実装自体はVoiceOverローターと同様
- accessibilityCustomRotorsを使用すればフィルタリングして特定の項目に絞れる
// Custom text rotor
func customRotor(for attribute: NSAttributedString.Key) -> UIAccessibilityCustomRotor {
UIAccessibilityCustomRotor(name: attribute.rotorName) { [unowned self] predicate in
var targetRange: UITextRange? // Goal: find the range of following `attribute`
let beginningRange = ...
guard let currentRange = ... else { return nil }
let searchRange: NSRange, searchOptions: NSAttributedString.EnumerationOptions
switch predicate.searchDirection { }
self.attributedText.enumerateAttribute(
attribute, in: searchRange, options: searchOptions) { value, range, stop in
guard value != nil else { return }
targetRange = self.textRange(from: range)
stop.pointee = true
}
...
// targetRangeをnil値にして最初か最後にいることを示す
return UIAccessibilityCustomRotorItemResult(targetElement: self,
targetRange: targetRange)
}
}
VoiceOverの効率向上に必要なこと
- インタフェースの視覚的に最も複雑な領域の特定
- VoiceOverを使った場合と使わないのと同じぐらい簡単か確認する
- 簡単でない場合は、画面が見えていない場合も同様なのでカスタムローターの利用を検討する