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 {
|
enum RotorType: String, CaseIterable {
|
||||||
|
|
||||||
case button = "Buttons"
|
case button = "Buttons"
|
||||||
|
case header = "Header"
|
||||||
|
|
||||||
var trait: UIAccessibilityTraits {
|
var trait: UIAccessibilityTraits {
|
||||||
switch self {
|
switch self {
|
||||||
case .button:
|
case .button:
|
||||||
return .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> = []
|
public var anyCancellable: Set<AnyCancellable> = []
|
||||||
private var delegateObj: MVMCoreUIDelegateObject?
|
private var delegateObj: MVMCoreUIDelegateObject?
|
||||||
private var currentRotorIndex: Int = 0
|
private var rotorIndexes: [RotorType: Int] = [:]
|
||||||
|
|
||||||
required public init(model: PageBehaviorModelProtocol, delegateObject: MVMCoreUIDelegateObject?) { }
|
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() {
|
private func identifyAndPrepareRotors() {
|
||||||
var rotorElements: [UIAccessibilityCustomRotor] = []
|
var rotorElements: [UIAccessibilityCustomRotor] = []
|
||||||
let currentViewController = ((delegateObj?.moleculeDelegate as? MVMCoreViewManagerViewControllerProtocol)?.manager as? SubNavManagerController) ?? (delegateObj?.moleculeDelegate as? ViewController)
|
let currentViewController = ((delegateObj?.moleculeDelegate as? MVMCoreViewManagerViewControllerProtocol)?.manager as? SubNavManagerController) ?? (delegateObj?.moleculeDelegate as? ViewController)
|
||||||
for type in RotorType.allCases {
|
for type in RotorType.allCases {
|
||||||
if let elements = getTraitMappedElements(template: currentViewController, type: type),
|
if let elements = getTraitMappedElements(currentViewController, type: type),
|
||||||
let rotor = createRotor(elements, for: type) {
|
let rotor = createRotor(elements, for: type) {
|
||||||
rotorElements.append(rotor)
|
rotorElements.append(rotor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
currentViewController?.navigationController?.accessibilityCustomRotors = rotorElements
|
currentViewController?.navigationController?.accessibilityCustomRotors = rotorElements
|
||||||
}
|
}
|
||||||
|
|
||||||
private func getTraitMappedElements(template: ViewController?, type: RotorType) -> [Any]? {
|
private func getTraitMappedElements(_ template: ViewController?, type: RotorType) -> [Any]? {
|
||||||
var accessibilityElements: [Any?]? = template?.navigationItem.leftBarButtonItems ?? []
|
var accessibilityElements: [Any?] = []
|
||||||
accessibilityElements?.append(contentsOf: template?.navigationItem.rightBarButtonItems ?? [])
|
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 {
|
if let tabs = (template as? SubNavManagerController)?.tabs {
|
||||||
accessibilityElements?.append(contentsOf: tabs.subviews.filter {
|
accessibilityElements.append(contentsOf: tabs.subviews.filter {
|
||||||
$0.accessibilityTraits.contains(type.trait)
|
$0.accessibilityTraits.contains(type.trait)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if let rotorElements = getRotorElementsFrom(template: template, type: type) {
|
if let rotorElements = getRotorElementsFrom(template: template, type: type) {
|
||||||
accessibilityElements?.append(contentsOf: rotorElements)
|
accessibilityElements.append(contentsOf: rotorElements)
|
||||||
}
|
}
|
||||||
if let tabBarHidden = (template as? TabPageModelProtocol)?.tabBarHidden, !tabBarHidden {
|
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)
|
$0.accessibilityTraits.contains(type.trait)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return accessibilityElements?.compactMap { $0 }
|
return accessibilityElements.compactMap { $0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
private func getRotorElementsFrom(template: ViewController?, type: RotorType) -> [Any]? {
|
private func getRotorElementsFrom(template: ViewController?, type: RotorType) -> [Any]? {
|
||||||
if let currentViewController = template as? MoleculeListTemplate,
|
if let currentViewController = template as? MoleculeListTemplate,
|
||||||
let templateModel = currentViewController.templateModel,
|
let templateModel = currentViewController.templateModel,
|
||||||
let tableView = currentViewController.tableView { //List templates
|
let tableView = currentViewController.tableView { //List templates
|
||||||
|
|
||||||
var rotorElements: [(model: MoleculeModelProtocol, indexPath: IndexPath)] = []
|
var rotorElements: [(model: MoleculeModelProtocol, indexPath: IndexPath)] = []
|
||||||
var currentIndexPath: IndexPath?
|
var currentIndexPath: IndexPath?
|
||||||
|
|
||||||
@ -301,7 +338,7 @@ open class AccessibilityHandlerBehavior: PageVisibilityBehavior {
|
|||||||
let otherInteractiveElements = currentViewController.view?.getMoleculeViews(excludedViews: [tableView, tableView.tableFooterView, tableView.tableHeaderView]
|
let otherInteractiveElements = currentViewController.view?.getMoleculeViews(excludedViews: [tableView, tableView.tableFooterView, tableView.tableHeaderView]
|
||||||
.compactMap { $0 }) { (subView: MoleculeViewProtocol, _) in
|
.compactMap { $0 }) { (subView: MoleculeViewProtocol, _) in
|
||||||
subView.accessibilityTraits.contains(type.trait)
|
subView.accessibilityTraits.contains(type.trait)
|
||||||
} as? [Any] ?? []
|
} as? [Any] ?? []
|
||||||
|
|
||||||
return headerViewElements + otherInteractiveElements + (rotorElements as [Any]) + footerViewElements
|
return headerViewElements + otherInteractiveElements + (rotorElements as [Any]) + footerViewElements
|
||||||
} else if let currentViewController = template as? MoleculeStackTemplate { //Stack templates
|
} 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? {
|
private func createRotor(_ elements: [Any], for type: RotorType) -> UIAccessibilityCustomRotor? {
|
||||||
guard elements.isEmpty, let tableView = (delegateObj?.moleculeListDelegate as? MoleculeListTemplate)?.tableView else { return nil }
|
guard !elements.isEmpty else { return nil }
|
||||||
return UIAccessibilityCustomRotor(name: type.rawValue) { [weak self] predicate in
|
return type.getUIAccessibilityCustomRotor { [weak self] predicate in
|
||||||
guard let self else { return UIAccessibilityCustomRotorItemResult() }
|
guard let self else { return UIAccessibilityCustomRotorItemResult() }
|
||||||
|
var rotorIndex = self.rotorIndexes[type] ?? 0
|
||||||
if predicate.searchDirection == .next {
|
if predicate.searchDirection == .next {
|
||||||
self.currentRotorIndex += 1
|
rotorIndex += 1
|
||||||
if self.currentRotorIndex > elements.count {
|
if rotorIndex > elements.count {
|
||||||
self.currentRotorIndex = 1
|
rotorIndex = 1
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.currentRotorIndex -= 1
|
rotorIndex -= 1
|
||||||
if self.currentRotorIndex <= 0 {
|
if rotorIndex <= 0 {
|
||||||
self.currentRotorIndex = elements.count
|
rotorIndex = elements.count
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var rotorElement = elements[self.currentRotorIndex - 1]
|
var rotorElement = elements[rotorIndex - 1]
|
||||||
if let element = rotorElement as? (model: MoleculeModelProtocol, indexPath: IndexPath) {
|
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)
|
tableView.scrollToRow(at: element.indexPath, at: .middle, animated: false)
|
||||||
rotorElement = tableView.cellForRow(at: element.indexPath)?.getMoleculeViews { (subView: MoleculeViewProtocol, stop: inout Bool) in
|
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 }
|
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
|
return true
|
||||||
}.first as Any
|
}.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)
|
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
|
// MARK: - Helpers
|
||||||
|
|||||||
@ -35,6 +35,7 @@ open class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWat
|
|||||||
public var size: Styler.Button.Size? = .standard
|
public var size: Styler.Button.Size? = .standard
|
||||||
public var groupName: String = ""
|
public var groupName: String = ""
|
||||||
public var inverted: Bool = false
|
public var inverted: Bool = false
|
||||||
|
public var accessibilityTraits: UIAccessibilityTraits?
|
||||||
|
|
||||||
public lazy var enabledColors: FacadeElements = (fill: enabled_fillColor(),
|
public lazy var enabledColors: FacadeElements = (fill: enabled_fillColor(),
|
||||||
text: enabled_textColor(),
|
text: enabled_textColor(),
|
||||||
@ -195,6 +196,7 @@ open class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWat
|
|||||||
case disabledTextColor
|
case disabledTextColor
|
||||||
case disabledBorderColor
|
case disabledBorderColor
|
||||||
case width
|
case width
|
||||||
|
case accessibilityTraits
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -207,6 +209,7 @@ open class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWat
|
|||||||
id = try typeContainer.decodeIfPresent(String.self, forKey: .id) ?? UUID().uuidString
|
id = try typeContainer.decodeIfPresent(String.self, forKey: .id) ?? UUID().uuidString
|
||||||
accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier)
|
accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier)
|
||||||
accessibilityText = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityText)
|
accessibilityText = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityText)
|
||||||
|
accessibilityTraits = try typeContainer.decodeIfPresent(UIAccessibilityTraits.self, forKey: .accessibilityTraits)
|
||||||
title = try typeContainer.decode(String.self, forKey: .title)
|
title = try typeContainer.decode(String.self, forKey: .title)
|
||||||
action = try typeContainer.decodeModel(codingKey: .action)
|
action = try typeContainer.decodeModel(codingKey: .action)
|
||||||
|
|
||||||
|
|||||||
@ -44,6 +44,7 @@ public typealias ActionBlock = () -> ()
|
|||||||
|
|
||||||
public var shouldMaskWhileRecording: Bool = false
|
public var shouldMaskWhileRecording: Bool = false
|
||||||
|
|
||||||
|
private var model: MoleculeModelProtocol?
|
||||||
//------------------------------------------------------
|
//------------------------------------------------------
|
||||||
// MARK: - Multi-Action Text
|
// MARK: - Multi-Action Text
|
||||||
//------------------------------------------------------
|
//------------------------------------------------------
|
||||||
@ -408,6 +409,7 @@ public typealias ActionBlock = () -> ()
|
|||||||
attributedText = attributedString
|
attributedText = attributedString
|
||||||
originalAttributedString = attributedText
|
originalAttributedString = attributedText
|
||||||
}
|
}
|
||||||
|
self.model = labelModel
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc public static func setUILabel(_ label: UILabel?, withJSON json: [AnyHashable: Any]?, delegate: DelegateObject?, additionalData: [AnyHashable: Any]?) {
|
@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
|
return range
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension Label: MoleculeViewModelProtocol {
|
||||||
|
|
||||||
|
public var moleculeModel: MoleculeModelProtocol? { model }
|
||||||
|
}
|
||||||
|
|||||||
@ -109,7 +109,7 @@ public protocol MoleculeViewModelProtocol: UIView {
|
|||||||
|
|
||||||
public extension MoleculeViewModelProtocol {
|
public extension MoleculeViewModelProtocol {
|
||||||
|
|
||||||
public var moleculeModel: MoleculeModelProtocol? {
|
var moleculeModel: MoleculeModelProtocol? {
|
||||||
get { nil }
|
get { nil }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -107,6 +107,10 @@ public typealias ButtonAction = (Button) -> ()
|
|||||||
isEnabled = model.enabled
|
isEnabled = model.enabled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let accessibilityTraits = model.accessibilityTraits {
|
||||||
|
self.accessibilityTraits = accessibilityTraits
|
||||||
|
}
|
||||||
|
|
||||||
guard let model = model as? ButtonModelProtocol else { return }
|
guard let model = model as? ButtonModelProtocol else { return }
|
||||||
|
|
||||||
set(with: model.action, delegateObject: delegateObject, additionalData: additionalData)
|
set(with: model.action, delegateObject: delegateObject, additionalData: additionalData)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user