added heading level rotor
This commit is contained in:
parent
c33fbfba90
commit
46fb714d34
@ -197,18 +197,32 @@ open class AccessibilityHandlerBehavior: PageVisibilityBehavior {
|
||||
enum RotorType: String, CaseIterable {
|
||||
|
||||
case button = "Buttons"
|
||||
case header = "Header"
|
||||
|
||||
var trait: UIAccessibilityTraits {
|
||||
switch self {
|
||||
case .button:
|
||||
return .button
|
||||
case .header:
|
||||
return .header
|
||||
}
|
||||
}
|
||||
|
||||
func getUIAccessibilityCustomRotor(itemSearch: @escaping UIAccessibilityCustomRotor.Search) -> UIAccessibilityCustomRotor? {
|
||||
var accessibilityCustomRotor: UIAccessibilityCustomRotor?
|
||||
switch self {
|
||||
case .header:
|
||||
accessibilityCustomRotor = UIAccessibilityCustomRotor(systemType: .heading, itemSearch: itemSearch)
|
||||
default:
|
||||
accessibilityCustomRotor = UIAccessibilityCustomRotor(name: rawValue, itemSearch: itemSearch)
|
||||
}
|
||||
return accessibilityCustomRotor
|
||||
}
|
||||
}
|
||||
|
||||
public var anyCancellable: Set<AnyCancellable> = []
|
||||
private var delegateObj: MVMCoreUIDelegateObject?
|
||||
private var currentRotorIndex: Int = 0
|
||||
private var rotorIndexes: [RotorType: Int] = [:]
|
||||
|
||||
required public init(model: PageBehaviorModelProtocol, delegateObject: MVMCoreUIDelegateObject?) { }
|
||||
|
||||
@ -239,43 +253,66 @@ open class AccessibilityHandlerBehavior: PageVisibilityBehavior {
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - Private Methods
|
||||
//MARK: - Accessibility Methods
|
||||
|
||||
private func updateAccessibilityViews(_ delegateObject: MVMCoreUIDelegateObject?) {
|
||||
var accessibilityElements: [Any?] = [MVMCoreUISplitViewController.main()?.topAlertView]
|
||||
if let managerController = (delegateObject?.moleculeDelegate as? MVMCoreViewManagerViewControllerProtocol)?.manager as? SubNavManagerController {
|
||||
accessibilityElements.append(managerController.navigationController)
|
||||
accessibilityElements.append(managerController.tabs)
|
||||
accessibilityElements.append(contentsOf: managerController.view.subviews)
|
||||
accessibilityElements.append(MVMCoreUISplitViewController.main()?.tabBar)
|
||||
managerController.view.accessibilityElements = accessibilityElements.compactMap { $0 }
|
||||
} else if let controller = delegateObject?.moleculeDelegate as? UIViewController {
|
||||
accessibilityElements.append(controller.navigationController)
|
||||
accessibilityElements.append(contentsOf: controller.view.subviews.reversed())
|
||||
accessibilityElements.append(MVMCoreUISplitViewController.main()?.tabBar)
|
||||
controller.view.accessibilityElements = accessibilityElements.compactMap { $0 }
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - Rotor Methods
|
||||
private func identifyAndPrepareRotors() {
|
||||
var rotorElements: [UIAccessibilityCustomRotor] = []
|
||||
let currentViewController = ((delegateObj?.moleculeDelegate as? MVMCoreViewManagerViewControllerProtocol)?.manager as? SubNavManagerController) ?? (delegateObj?.moleculeDelegate as? ViewController)
|
||||
for type in RotorType.allCases {
|
||||
if let elements = getTraitMappedElements(template: currentViewController, type: type),
|
||||
let rotor = createRotor(elements, for: type) {
|
||||
if let elements = getTraitMappedElements(currentViewController, type: type),
|
||||
let rotor = createRotor(elements, for: type) {
|
||||
rotorElements.append(rotor)
|
||||
}
|
||||
}
|
||||
currentViewController?.navigationController?.accessibilityCustomRotors = rotorElements
|
||||
}
|
||||
|
||||
private func getTraitMappedElements(template: ViewController?, type: RotorType) -> [Any]? {
|
||||
var accessibilityElements: [Any?]? = template?.navigationItem.leftBarButtonItems ?? []
|
||||
accessibilityElements?.append(contentsOf: template?.navigationItem.rightBarButtonItems ?? [])
|
||||
private func getTraitMappedElements(_ template: ViewController?, type: RotorType) -> [Any]? {
|
||||
var accessibilityElements: [Any?] = []
|
||||
switch type {
|
||||
case .button:
|
||||
accessibilityElements.append(contentsOf: template?.navigationItem.leftBarButtonItems ?? [])
|
||||
accessibilityElements.append(contentsOf: template?.navigationItem.rightBarButtonItems ?? [])
|
||||
case .header:
|
||||
accessibilityElements.append(template?.navigationItem.titleView)
|
||||
}
|
||||
if let tabs = (template as? SubNavManagerController)?.tabs {
|
||||
accessibilityElements?.append(contentsOf: tabs.subviews.filter {
|
||||
accessibilityElements.append(contentsOf: tabs.subviews.filter {
|
||||
$0.accessibilityTraits.contains(type.trait)
|
||||
})
|
||||
}
|
||||
if let rotorElements = getRotorElementsFrom(template: template, type: type) {
|
||||
accessibilityElements?.append(contentsOf: rotorElements)
|
||||
accessibilityElements.append(contentsOf: rotorElements)
|
||||
}
|
||||
if let tabBarHidden = (template as? TabPageModelProtocol)?.tabBarHidden, !tabBarHidden {
|
||||
accessibilityElements?.append(contentsOf: (MVMCoreUISplitViewController.main()?.tabBar?.subviews ?? []).filter {
|
||||
accessibilityElements.append(contentsOf: (MVMCoreUISplitViewController.main()?.tabBar?.subviews ?? []).filter {
|
||||
$0.accessibilityTraits.contains(type.trait)
|
||||
})
|
||||
}
|
||||
return accessibilityElements?.compactMap { $0 }
|
||||
return accessibilityElements.compactMap { $0 }
|
||||
}
|
||||
|
||||
private func getRotorElementsFrom(template: ViewController?, type: RotorType) -> [Any]? {
|
||||
if let currentViewController = template as? MoleculeListTemplate,
|
||||
let templateModel = currentViewController.templateModel,
|
||||
let tableView = currentViewController.tableView { //List templates
|
||||
|
||||
let templateModel = currentViewController.templateModel,
|
||||
let tableView = currentViewController.tableView { //List templates
|
||||
var rotorElements: [(model: MoleculeModelProtocol, indexPath: IndexPath)] = []
|
||||
var currentIndexPath: IndexPath?
|
||||
|
||||
@ -301,7 +338,7 @@ open class AccessibilityHandlerBehavior: PageVisibilityBehavior {
|
||||
let otherInteractiveElements = currentViewController.view?.getMoleculeViews(excludedViews: [tableView, tableView.tableFooterView, tableView.tableHeaderView]
|
||||
.compactMap { $0 }) { (subView: MoleculeViewProtocol, _) in
|
||||
subView.accessibilityTraits.contains(type.trait)
|
||||
} as? [Any] ?? []
|
||||
} as? [Any] ?? []
|
||||
|
||||
return headerViewElements + otherInteractiveElements + (rotorElements as [Any]) + footerViewElements
|
||||
} else if let currentViewController = template as? MoleculeStackTemplate { //Stack templates
|
||||
@ -313,22 +350,24 @@ open class AccessibilityHandlerBehavior: PageVisibilityBehavior {
|
||||
}
|
||||
|
||||
private func createRotor(_ elements: [Any], for type: RotorType) -> UIAccessibilityCustomRotor? {
|
||||
guard elements.isEmpty, let tableView = (delegateObj?.moleculeListDelegate as? MoleculeListTemplate)?.tableView else { return nil }
|
||||
return UIAccessibilityCustomRotor(name: type.rawValue) { [weak self] predicate in
|
||||
guard !elements.isEmpty else { return nil }
|
||||
return type.getUIAccessibilityCustomRotor { [weak self] predicate in
|
||||
guard let self else { return UIAccessibilityCustomRotorItemResult() }
|
||||
var rotorIndex = self.rotorIndexes[type] ?? 0
|
||||
if predicate.searchDirection == .next {
|
||||
self.currentRotorIndex += 1
|
||||
if self.currentRotorIndex > elements.count {
|
||||
self.currentRotorIndex = 1
|
||||
rotorIndex += 1
|
||||
if rotorIndex > elements.count {
|
||||
rotorIndex = 1
|
||||
}
|
||||
} else {
|
||||
self.currentRotorIndex -= 1
|
||||
if self.currentRotorIndex <= 0 {
|
||||
self.currentRotorIndex = elements.count
|
||||
rotorIndex -= 1
|
||||
if rotorIndex <= 0 {
|
||||
rotorIndex = elements.count
|
||||
}
|
||||
}
|
||||
var rotorElement = elements[self.currentRotorIndex - 1]
|
||||
if let element = rotorElement as? (model: MoleculeModelProtocol, indexPath: IndexPath) {
|
||||
var rotorElement = elements[rotorIndex - 1]
|
||||
if let tableView = (self.delegateObj?.moleculeListDelegate as? MoleculeListTemplate)?.tableView,
|
||||
let element = rotorElement as? (model: MoleculeModelProtocol, indexPath: IndexPath) { //for List templates
|
||||
tableView.scrollToRow(at: element.indexPath, at: .middle, animated: false)
|
||||
rotorElement = tableView.cellForRow(at: element.indexPath)?.getMoleculeViews { (subView: MoleculeViewProtocol, stop: inout Bool) in
|
||||
guard subView.accessibilityTraits.contains(type.trait), (subView as? MoleculeViewModelProtocol)?.moleculeModel?.id == element.model.id else { return false }
|
||||
@ -336,26 +375,11 @@ open class AccessibilityHandlerBehavior: PageVisibilityBehavior {
|
||||
return true
|
||||
}.first as Any
|
||||
}
|
||||
UIAccessibility.post(notification: .layoutChanged, argument: rotorElement)
|
||||
self.rotorIndexes[type] = rotorIndex
|
||||
AccessibilityHandler.shared()?.post(notification: .layoutChanged, argument: rotorElement)
|
||||
return UIAccessibilityCustomRotorItemResult(targetElement: rotorElement as! NSObjectProtocol, targetRange: nil)
|
||||
}
|
||||
}
|
||||
|
||||
private func updateAccessibilityViews(_ delegateObject: MVMCoreUIDelegateObject?) {
|
||||
var accessibilityElements: [Any?] = [MVMCoreUISplitViewController.main()?.topAlertView]
|
||||
if let managerController = (delegateObject?.moleculeDelegate as? MVMCoreViewManagerViewControllerProtocol)?.manager as? SubNavManagerController {
|
||||
accessibilityElements.append(managerController.navigationController)
|
||||
accessibilityElements.append(managerController.tabs)
|
||||
accessibilityElements.append(contentsOf: managerController.view.subviews)
|
||||
accessibilityElements.append(MVMCoreUISplitViewController.main()?.tabBar)
|
||||
managerController.view.accessibilityElements = accessibilityElements.compactMap { $0 }
|
||||
} else if let controller = delegateObject?.moleculeDelegate as? UIViewController {
|
||||
accessibilityElements.append(controller.navigationController)
|
||||
accessibilityElements.append(contentsOf: controller.view.subviews.reversed())
|
||||
accessibilityElements.append(MVMCoreUISplitViewController.main()?.tabBar)
|
||||
controller.view.accessibilityElements = accessibilityElements.compactMap { $0 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
@ -35,6 +35,7 @@ open class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWat
|
||||
public var size: Styler.Button.Size? = .standard
|
||||
public var groupName: String = ""
|
||||
public var inverted: Bool = false
|
||||
public var accessibilityTraits: UIAccessibilityTraits?
|
||||
|
||||
public lazy var enabledColors: FacadeElements = (fill: enabled_fillColor(),
|
||||
text: enabled_textColor(),
|
||||
@ -195,6 +196,7 @@ open class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWat
|
||||
case disabledTextColor
|
||||
case disabledBorderColor
|
||||
case width
|
||||
case accessibilityTraits
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
@ -207,6 +209,7 @@ open class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWat
|
||||
id = try typeContainer.decodeIfPresent(String.self, forKey: .id) ?? UUID().uuidString
|
||||
accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier)
|
||||
accessibilityText = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityText)
|
||||
accessibilityTraits = try typeContainer.decodeIfPresent(UIAccessibilityTraits.self, forKey: .accessibilityTraits)
|
||||
title = try typeContainer.decode(String.self, forKey: .title)
|
||||
action = try typeContainer.decodeModel(codingKey: .action)
|
||||
|
||||
|
||||
@ -44,6 +44,7 @@ public typealias ActionBlock = () -> ()
|
||||
|
||||
public var shouldMaskWhileRecording: Bool = false
|
||||
|
||||
private var model: MoleculeModelProtocol?
|
||||
//------------------------------------------------------
|
||||
// MARK: - Multi-Action Text
|
||||
//------------------------------------------------------
|
||||
@ -408,6 +409,7 @@ public typealias ActionBlock = () -> ()
|
||||
attributedText = attributedString
|
||||
originalAttributedString = attributedText
|
||||
}
|
||||
self.model = labelModel
|
||||
}
|
||||
|
||||
@objc public static func setUILabel(_ label: UILabel?, withJSON json: [AnyHashable: Any]?, delegate: DelegateObject?, additionalData: [AnyHashable: Any]?) {
|
||||
@ -1027,3 +1029,8 @@ func validateAttribute(range: NSRange, in string: NSAttributedString, type: Stri
|
||||
|
||||
return range
|
||||
}
|
||||
|
||||
extension Label: MoleculeViewModelProtocol {
|
||||
|
||||
public var moleculeModel: MoleculeModelProtocol? { model }
|
||||
}
|
||||
|
||||
@ -109,7 +109,7 @@ public protocol MoleculeViewModelProtocol: UIView {
|
||||
|
||||
public extension MoleculeViewModelProtocol {
|
||||
|
||||
public var moleculeModel: MoleculeModelProtocol? {
|
||||
var moleculeModel: MoleculeModelProtocol? {
|
||||
get { nil }
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,6 +107,10 @@ public typealias ButtonAction = (Button) -> ()
|
||||
isEnabled = model.enabled
|
||||
}
|
||||
|
||||
if let accessibilityTraits = model.accessibilityTraits {
|
||||
self.accessibilityTraits = accessibilityTraits
|
||||
}
|
||||
|
||||
guard let model = model as? ButtonModelProtocol else { return }
|
||||
|
||||
set(with: model.action, delegateObject: delegateObject, additionalData: additionalData)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user