vds_ios_sample/VDSSample/Protocols/CustomRotorable.swift
Matt Bruce 883f9fffb4 updated rotor logic
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2023-08-24 16:02:20 -05:00

123 lines
3.9 KiB
Swift

//
// AccessibilityCustomRotorable.swift
// VDS
//
// Created by Matt Bruce on 8/24/23.
//
import Foundation
import UIKit
public protocol CustomRotorable: UIViewController {
var customRotors: [CustomRotorType] { get set }
}
extension CustomRotorable {
/// Adds CustomRotor to the accessibilityCustomRotors array.
/// - Parameters:
/// - name: Name that will show up in the Rotor picker.
/// - trait: Any UIView that has this traits will be added to this Name.
internal func addCustomRotor(with name: String, for trait: UIAccessibilityTraits) {
//filter out old rotors with same name
accessibilityCustomRotors = (accessibilityCustomRotors ?? []).filter { $0.name != name }
//create new rotor
let newRotor = AccessibilityCustomRotor(with: name, for: trait, rootView: self.view)
//append rotor
accessibilityCustomRotors?.append(newRotor)
}
/// Loads all of the custom rotors for the screen.
public func loadCustomRotors() {
customRotors.forEach { addCustomRotor(with: $0.name, for: $0.trait) }
}
}
private class AccessibilityCustomRotor: UIAccessibilityCustomRotor {
var views: [UIView]?
var trait: UIAccessibilityTraits
weak var rootView: UIView?
init (with name: String, for trait: UIAccessibilityTraits, rootView: UIView){
self.rootView = rootView
self.trait = trait
super.init(name: name, itemSearch: { _ in return nil })
self.updateSearch()
}
func updateSearch() {
itemSearchBlock = { [weak self] predicate in
guard let self, let rootView = self.rootView else { return nil }
//create the view accessibleElements cache if it doesn't exist
if self.views == nil {
self.views = rootView.accessibleElements(with: trait)
}
guard let views = self.views, !views.isEmpty else { return nil }
let currentIndex = views.firstIndex(where: { $0 === predicate.currentItem.targetElement })
let count = views.count
//find the nextIndex
let nextIndex: Int
switch predicate.searchDirection {
case .next:
if let currentIndex, currentIndex != count - 1{
//go forwards
nextIndex = currentIndex + 1
} else {
//get the first
nextIndex = 0
}
case .previous:
if let currentIndex, currentIndex != 0 {
//go backwards
nextIndex = currentIndex - 1
} else {
//get the last
nextIndex = count - 1
}
@unknown default:
//get the first
nextIndex = 0
}
return UIAccessibilityCustomRotorItemResult(targetElement: views[nextIndex], targetRange: nil)
}
}
}
public extension UIView {
/// Gets all of the Views that has the matching accessibilityTrait.
/// - Parameter trait: This is the trailt for the accessibilityTrait property of a view.
/// - Returns: An array of RotorItemResult
func accessibleElements(with trait: UIAccessibilityTraits) -> [UIView] {
var elements: [UIView] = []
//add your self if you meet the requirements
if isAccessibilityElement, accessibilityTraits.contains(trait) {
elements.append(self)
}
//loop through your subviews
subviews.forEach { elements.append(contentsOf: $0.accessibleElements(with: trait)) }
return elements
}
}
public struct CustomRotorType {
public var name: String
public var trait: UIAccessibilityTraits
public init(name: String, trait: UIAccessibilityTraits) {
self.name = name
self.trait = trait
}
}