added heading level rotor

This commit is contained in:
Krishna Kishore Bandaru 2023-09-27 20:53:50 +05:30
parent c33fbfba90
commit 46fb714d34
5 changed files with 81 additions and 43 deletions

View File

@ -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

View File

@ -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)

View File

@ -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 }
}

View File

@ -109,7 +109,7 @@ public protocol MoleculeViewModelProtocol: UIView {
public extension MoleculeViewModelProtocol {
public var moleculeModel: MoleculeModelProtocol? {
var moleculeModel: MoleculeModelProtocol? {
get { nil }
}
}

View File

@ -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)