-
VoiceOver efficiency with custom rotors
Discover how you can integrate custom rotors and help people who use VoiceOver navigate complex situations within your app. Learn how custom rotors can help people explore even the most intricate interfaces, explore how to implement a custom rotor, and find out how rotors can improve navigation for someone who relies on VoiceOver.
To get the most out of this session, you should be familiar with general accessibility principles and VoiceOver accessibility APIs on iOS and iPadOS. For an overview, watch “Making Apps More Accessible with Custom Actions.”Recursos
Vídeos relacionados
WWDC23
WWDC21
WWDC19
-
Buscar neste vídeo...
-
-
4:04 - mapView.accessibilityCustomRotors = [customRotor(for: .stores), customRotor(for: .parks)]
mapView.accessibilityCustomRotors = [customRotor(for: .stores), customRotor(for: .parks)] -
4:31 - map rotor 1
// Custom map rotors func customRotor(for poiType: POI) -> UIAccessibilityCustomRotor { UIAccessibilityCustomRotor(name: poiType.rotorName) { [unowned self] predicate in return UIAccessibilityCustomRotorItemResult( ) } } -
4:56 - map rotor 2
// 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 } return UIAccessibilityCustomRotorItemResult( ) } } -
5:04 - map rotor 3
// 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 } return UIAccessibilityCustomRotorItemResult( ) } } -
5:17 - Maps rotor 4
// 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) } } -
8:07 - Text rotor 1
// 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 } switch predicate.searchDirection { } return UIAccessibilityCustomRotorItemResult(targetElement: self, targetRange: targetRange) } } -
8:20 - Text rotor 2
// 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 = self.textRange(from: self.beginningOfDocument, to: self.beginningOfDocument) guard let currentRange = predicate.currentItem.targetRange ?? beginningRange else { return nil } let searchRange: NSRange, searchOptions: NSAttributedString.EnumerationOptions switch predicate.searchDirection { } return UIAccessibilityCustomRotorItemResult(targetElement: self, targetRange: targetRange) } } -
8:37 - Text rotor 3
// 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 { case .previous: searchRange = self.rangeOfAttributedTextBefore(currentRange) searchOptions = [.reverse] case .next: searchRange = self.rangeOfAttributedTextAfter(currentRange) searchOptions = [] } return UIAccessibilityCustomRotorItemResult(targetElement: self, targetRange: targetRange) } } -
9:06 - Text rotor 4 (end)
// 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 } return UIAccessibilityCustomRotorItemResult(targetElement: self, targetRange: targetRange) } }
-