- 로터의 이점
- 복잡한 뷰 계층을 쉽게 이동할 수 있게 한다.
- 관련된 요소를 쉽게 찾을 수 있도록 한다.
- 비주얼 요소를 뽑아내고, 이를 적절히 분류하는 과정이 필요하다.
mapView.accessibilityCustomRotors = [customRotor(for: .stores), customRoter(for: .parks)]
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 }
return UIAccessibilityCustomRotorItemResult(targetElement: annotations[targetIndex], targetRange: nil)
}
}
func customRotor(for attribute: NSAttributedString.Key) -> UIAccessibilityCustomRotor {
UIAccessibilityCustomRotor(name: attribute.rotorName) { [unowned self] predicate in
var targetRange: UITextRange?
let beginningRange = self.textRange(from: self.beginingOfDocument,
to: self.beginingOfDocument)
guard let currentRange = predicate.currentItem.targetRange ?? beginningRange else {
return nil
}
let searchRange: NSRange, searchOptions: NSAttributedString.EnumeratedOptions
switch predicate.searchDirection {
case .previous:
searchRange = self.rangeOfAttributedTextBefore(currentRange)
searchOptions = [.reverse]
case .next:
searchRange = self.rangeOfAttributedTextAfter(currentRange)
searchOptions = []
}
self.attributedText.enumeratedAttribute(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)
}
}