addressed review comments & added model property to MoleculeViewProtocol
This commit is contained in:
parent
88505e704c
commit
8dc475ffdd
@ -32,7 +32,13 @@ public class AccessbilityOperation: MVMCoreOperation {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
UIAccessibility.post(notification: self.notificationType, argument: self.argument)
|
UIAccessibility.post(notification: self.notificationType, argument: self.argument)
|
||||||
self.markAsFinished()
|
if self.notificationType == .announcement {
|
||||||
|
NotificationCenter.default.addObserver(forName: UIAccessibility.announcementDidFinishNotification, object: nil, queue: .main) { _ in
|
||||||
|
self.markAsFinished()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.markAsFinished()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,125 +50,6 @@ public class AccessbilityOperation: MVMCoreOperation {
|
|||||||
|
|
||||||
open class AccessibilityHandler {
|
open class AccessibilityHandler {
|
||||||
|
|
||||||
public static func shared() -> Self? {
|
|
||||||
guard let shared = CoreUIObject.sharedInstance()?.accessibilityHandler else { return nil }
|
|
||||||
return MVMCoreActionUtility.fatalClassCheck(object: shared)
|
|
||||||
}
|
|
||||||
|
|
||||||
public var previousAccessiblityElement: Any?
|
|
||||||
public var anyCancellable: Set<AnyCancellable> = []
|
|
||||||
public weak var delegate: MVMCoreViewControllerProtocol?
|
|
||||||
|
|
||||||
private var accessibilityOperationQueue: OperationQueue = {
|
|
||||||
let queue = OperationQueue()
|
|
||||||
queue.maxConcurrentOperationCount = 1
|
|
||||||
return queue
|
|
||||||
}()
|
|
||||||
public var accessibilityId: String?
|
|
||||||
public var hasTopNotificationInPage: Bool = false
|
|
||||||
|
|
||||||
public init() {
|
|
||||||
registerForPageChanges()
|
|
||||||
registerForFocusChanges()
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Register with Accessibility Handler listeners
|
|
||||||
private func registerForFocusChanges() {
|
|
||||||
//Since foucs shifted to other elements cancelling existing focus shift notifications if any
|
|
||||||
NotificationCenter.default.publisher(for: UIAccessibility.elementFocusedNotification)
|
|
||||||
.sink { [weak self] _ in
|
|
||||||
self?.cancelAllOperations()
|
|
||||||
}.store(in: &anyCancellable)
|
|
||||||
}
|
|
||||||
|
|
||||||
func registerForTopNotificationsChanges() {
|
|
||||||
NotificationHandler.shared()?.onNotificationWillShow
|
|
||||||
.sink { [weak self] (_, model) in
|
|
||||||
if self?.previousAccessiblityElement == nil {
|
|
||||||
self?.capturePreviousFocusElement()
|
|
||||||
}
|
|
||||||
}.store(in: &anyCancellable)
|
|
||||||
NotificationHandler.shared()?.onNotificationShown
|
|
||||||
.sink { [weak self] (view, model) in
|
|
||||||
self?.post(notification: .layoutChanged, argument: view)
|
|
||||||
self?.hasTopNotificationInPage = false
|
|
||||||
}.store(in: &anyCancellable)
|
|
||||||
NotificationHandler.shared()?.onNotificationWillDismiss
|
|
||||||
.sink { [weak self] (view, model) in
|
|
||||||
self?.post(notification: .announcement, argument: MVMCoreUIUtility.hardcodedString(withKey: "AccTopAlertClosed"))
|
|
||||||
}.store(in: &anyCancellable)
|
|
||||||
NotificationHandler.shared()?.onNotificationDismissed
|
|
||||||
.sink { [weak self] (view, model) in
|
|
||||||
self?.postAccessbilityToPrevElement()
|
|
||||||
}.store(in: &anyCancellable)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Registers to know when pages change.
|
|
||||||
open func registerForPageChanges() {
|
|
||||||
NavigationHandler.shared()
|
|
||||||
.onNavigation
|
|
||||||
.sink { [self] (event, operation) in
|
|
||||||
switch event {
|
|
||||||
case .willNavigate:
|
|
||||||
willNavigate(operation)
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}.store(in: &anyCancellable)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func willNavigate(_ operation: NavigationOperation) {
|
|
||||||
previousAccessiblityElement = nil
|
|
||||||
if let subNavManagerController = (operation.toNavigationControllerViewControllers?.last as? MVMCoreViewManagerViewControllerProtocol)?.manager as? SubNavManagerController {
|
|
||||||
delegate = subNavManagerController.getCurrentViewController() as? MVMCoreViewControllerProtocol
|
|
||||||
} else {
|
|
||||||
delegate = operation.toNavigationControllerViewControllers?.last as? MVMCoreViewControllerProtocol
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Accessibility Handler operation events
|
|
||||||
open func capturePreviousFocusElement() {
|
|
||||||
previousAccessiblityElement = UIAccessibility.focusedElement(using: .notificationVoiceOver)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func postAccessbilityToPrevElement() {
|
|
||||||
post(notification: .layoutChanged, argument: previousAccessiblityElement)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func add(operation: Operation) {
|
|
||||||
accessibilityOperationQueue.addOperation(operation)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func cancelAllOperations() {
|
|
||||||
accessibilityOperationQueue.cancelAllOperations()
|
|
||||||
}
|
|
||||||
|
|
||||||
public func post(notification type: UIAccessibility.Notification, argument: Any? = nil) {
|
|
||||||
guard UIAccessibility.isVoiceOverRunning else { return }
|
|
||||||
let accessbilityOperation = AccessbilityOperation(notificationType: type, argument: argument)
|
|
||||||
add(operation: accessbilityOperation)
|
|
||||||
}
|
|
||||||
|
|
||||||
//To get first focus element on the screen
|
|
||||||
open func getFirstFocusedElementOnScreen() -> Any? {
|
|
||||||
(delegate as? UIViewController)?.navigationController?.navigationBar
|
|
||||||
}
|
|
||||||
|
|
||||||
//Subclass can decide to trigger Accessibility notification on screen change.
|
|
||||||
open func canPostAccessbilityNotification(for viewController: UIViewController) -> Bool { true }
|
|
||||||
|
|
||||||
func getPreDefinedFocusedElementIfAny() -> UIView? {
|
|
||||||
guard let accessibilityId, let view = (delegate as? UIViewController)?.view else { return nil }
|
|
||||||
return MVMCoreUIUtility.findViews(by: MoleculeViewProtocol.self, views: [view]).first {
|
|
||||||
($0 as? MoleculeViewModelProtocol)?.moleculeModel?.id == accessibilityId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Accessibility Handler Behaviour
|
|
||||||
///Accessibility Handler Behaviour to detect page shown and post notification to first interactive element on screen or the pre-defined focused element.
|
|
||||||
open class AccessibilityHandlerBehavior: PageVisibilityBehavior, PageMoleculeTransformationBehavior {
|
|
||||||
|
|
||||||
enum RotorType: String, CaseIterable {
|
enum RotorType: String, CaseIterable {
|
||||||
|
|
||||||
case button = "Buttons"
|
case button = "Buttons"
|
||||||
@ -189,72 +76,121 @@ open class AccessibilityHandlerBehavior: PageVisibilityBehavior, PageMoleculeTra
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var anyCancellable: Set<AnyCancellable> = []
|
public static func shared() -> Self? {
|
||||||
private var delegateObj: MVMCoreUIDelegateObject?
|
guard let shared = CoreUIObject.sharedInstance()?.accessibilityHandler else { return nil }
|
||||||
private var rotorIndexes: [RotorType: Int] = [:]
|
return MVMCoreActionUtility.fatalClassCheck(object: shared)
|
||||||
|
}
|
||||||
|
|
||||||
required public init(model: PageBehaviorModelProtocol, delegateObject: MVMCoreUIDelegateObject?) { }
|
public var accessibilityId: String?
|
||||||
|
public var previousAccessiblityElement: Any?
|
||||||
|
public var anyCancellable: Set<AnyCancellable> = []
|
||||||
|
public weak var delegate: MVMCoreViewControllerProtocol?
|
||||||
|
private var rotorIndexes: [RotorType: Int] = [:]
|
||||||
|
private var hasTopNotificationInPage: Bool { NotificationHandler.shared()?.isNotificationShowing() ?? false }
|
||||||
|
private let accessibilityOperationQueue: OperationQueue = {
|
||||||
|
let queue = OperationQueue()
|
||||||
|
queue.maxConcurrentOperationCount = 1
|
||||||
|
return queue
|
||||||
|
}()
|
||||||
|
|
||||||
|
public init() {
|
||||||
|
registerForFocusChanges()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Accessibility Handler operation events
|
||||||
|
open func capturePreviousFocusElement() {
|
||||||
|
previousAccessiblityElement = UIAccessibility.focusedElement(using: .notificationVoiceOver)
|
||||||
|
}
|
||||||
|
|
||||||
|
open func postAccessbilityToPrevElement() {
|
||||||
|
post(notification: .layoutChanged, argument: previousAccessiblityElement)
|
||||||
|
previousAccessiblityElement = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
public func post(notification type: UIAccessibility.Notification, argument: Any? = nil) {
|
||||||
|
guard UIAccessibility.isVoiceOverRunning else { return }
|
||||||
|
let accessbilityOperation = AccessbilityOperation(notificationType: type, argument: argument)
|
||||||
|
accessibilityOperationQueue.addOperation(accessbilityOperation)
|
||||||
|
}
|
||||||
|
|
||||||
|
//To get first focus element on the screen
|
||||||
|
open func getFirstFocusedElementOnScreen() -> Any? {
|
||||||
|
(delegate as? UIViewController)?.navigationController?.navigationBar
|
||||||
|
}
|
||||||
|
|
||||||
|
//Subclass can decide to trigger Accessibility notification on screen change.
|
||||||
|
open func canPostAccessbilityNotification(for viewController: UIViewController) -> Bool { true }
|
||||||
|
|
||||||
|
func getPreDefinedFocusedElementIfAny() -> UIView? {
|
||||||
|
guard let accessibilityId, let view = (delegate as? UIViewController)?.view else { return nil }
|
||||||
|
return MVMCoreUIUtility.findViews(by: MoleculeViewProtocol.self, views: [view]).first {
|
||||||
|
$0.model?.id == accessibilityId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension AccessibilityHandler {
|
||||||
|
|
||||||
public func onPageNew(rootMolecules: [MoleculeModelProtocol], _ delegateObject: MVMCoreUIDelegateObject?) {
|
public func onPageNew(rootMolecules: [MoleculeModelProtocol], _ delegateObject: MVMCoreUIDelegateObject?) {
|
||||||
|
rotorIndexes = [:]
|
||||||
|
previousAccessiblityElement = nil
|
||||||
guard let loadObject = (delegateObject?.loadDelegate as? MVMCoreViewControllerProtocol)?.loadObject else { return }
|
guard let loadObject = (delegateObject?.loadDelegate as? MVMCoreViewControllerProtocol)?.loadObject else { return }
|
||||||
AccessibilityHandler.shared()?.accessibilityId = loadObject?.pageJSON?.optionalStringForKey("accessibilityId")
|
accessibilityId = loadObject?.pageJSON?.optionalStringForKey("accessibilityId")
|
||||||
//TODO: - Need to revisit this logic
|
|
||||||
AccessibilityHandler.shared()?.hasTopNotificationInPage = loadObject?.responseJSON?.optionalDictionaryForKey("TopNotification") != nil || loadObject?.responseInfoMap?.optionalStringForKey("userMessage") != nil
|
|
||||||
if let announcementText = loadObject?.pageJSON?.optionalStringForKey("announcementText") {
|
if let announcementText = loadObject?.pageJSON?.optionalStringForKey("announcementText") {
|
||||||
AccessibilityHandler.shared()?.post(notification: .announcement, argument: announcementText)
|
post(notification: .announcement, argument: announcementText)
|
||||||
}
|
}
|
||||||
|
delegate = delegateObject?.moleculeDelegate as? MVMCoreViewControllerProtocol
|
||||||
}
|
}
|
||||||
|
|
||||||
//MARK: - PageVisibiltyBehaviour
|
//MARK: - PageVisibiltyBehaviour
|
||||||
open func willShowPage(_ delegateObject: MVMCoreUIDelegateObject?) {
|
public func willShowPage(_ delegateObject: MVMCoreUIDelegateObject?) {
|
||||||
updateAccessibilityViews(delegateObject)
|
updateAccessibilityViews(delegateObject)
|
||||||
guard let controller = delegateObject?.moleculeDelegate as? UIViewController,
|
guard let controller = delegateObject?.moleculeDelegate as? UIViewController,
|
||||||
(AccessibilityHandler.shared()?.canPostAccessbilityNotification(for: controller) ?? true),
|
canPostAccessbilityNotification(for: controller),
|
||||||
AccessibilityHandler.shared()?.accessibilityId == nil else { return }
|
accessibilityId == nil else { return }
|
||||||
if AccessibilityHandler.shared()?.hasTopNotificationInPage ?? false {
|
if hasTopNotificationInPage {
|
||||||
AccessibilityHandler.shared()?.previousAccessiblityElement = AccessibilityHandler.shared()?.getFirstFocusedElementOnScreen()
|
previousAccessiblityElement = getFirstFocusedElementOnScreen()
|
||||||
} else {
|
} else {
|
||||||
AccessibilityHandler.shared()?.post(notification: .layoutChanged, argument: AccessibilityHandler.shared()?.getFirstFocusedElementOnScreen())
|
post(notification: .layoutChanged, argument: getFirstFocusedElementOnScreen())
|
||||||
}
|
}
|
||||||
delegateObj = delegateObject
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///We need to shift focus to any element mentioned in server response i.e to retain focus of the element in new page, from where action is triggered.
|
///We need to shift focus to any element mentioned in server response i.e to retain focus of the element in new page, from where action is triggered.
|
||||||
///https://oneconfluence.verizon.com/display/MFD/Accessibility+-+Focus+Retain
|
///https://oneconfluence.verizon.com/display/MFD/Accessibility+-+Focus+Retain
|
||||||
open func onPageShown(_ delegateObject: MVMCoreUIDelegateObject?) {
|
public func onPageShown(_ delegateObject: MVMCoreUIDelegateObject?) {
|
||||||
updateAccessibilityViews(delegateObject) //To track FAB & HAB elements on UI
|
identifyAndPrepareRotors(delegateObject)
|
||||||
identifyAndPrepareRotors()
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||||
guard let accessibilityElement = AccessibilityHandler.shared()?.getPreDefinedFocusedElementIfAny() else { return }
|
(delegateObject?.moleculeDelegate as? UIViewController)?.view.accessibilityElements = nil
|
||||||
AccessibilityHandler.shared()?.accessibilityId = nil
|
}
|
||||||
if AccessibilityHandler.shared()?.hasTopNotificationInPage ?? false {
|
guard let accessibilityElement = getPreDefinedFocusedElementIfAny() else { return }
|
||||||
AccessibilityHandler.shared()?.previousAccessiblityElement = accessibilityElement
|
accessibilityId = nil
|
||||||
|
if hasTopNotificationInPage {
|
||||||
|
previousAccessiblityElement = accessibilityElement
|
||||||
} else {
|
} else {
|
||||||
AccessibilityHandler.shared()?.post(notification: .layoutChanged, argument: accessibilityElement)
|
post(notification: .layoutChanged, argument: accessibilityElement)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//MARK: - Accessibility Methods
|
//MARK: - Accessibility Methods
|
||||||
private func updateAccessibilityViews(_ delegateObject: MVMCoreUIDelegateObject?) {
|
private func updateAccessibilityViews(_ delegateObject: MVMCoreUIDelegateObject?) {
|
||||||
//TODO: - Need to revisit this logic
|
var currentController = delegateObject?.moleculeDelegate as? UIViewController
|
||||||
var accessibilityElements: [Any?] = [MVMCoreUISplitViewController.main()?.topAlertView]
|
var accessibilityElements: [Any?] = [MVMCoreUISplitViewController.main()?.topAlertView, MVMCoreUISplitViewController.main()?.navigationController]
|
||||||
if let managerController = (delegateObject?.moleculeDelegate as? MVMCoreViewManagerViewControllerProtocol)?.manager as? SubNavManagerController {
|
if let manager = ((delegateObject?.moleculeDelegate as? MVMCoreViewManagerViewControllerProtocol)?.manager as? MVMCoreViewManagerProtocol & UIViewController),
|
||||||
accessibilityElements.append(managerController.navigationController)
|
let managerAccessibilityElements = manager.getAccessibilityElements() {
|
||||||
accessibilityElements.append(managerController.tabs)
|
accessibilityElements.append(contentsOf: managerAccessibilityElements)
|
||||||
accessibilityElements.append(contentsOf: managerController.view.subviews)
|
accessibilityElements.append(contentsOf: manager.view.subviews)
|
||||||
accessibilityElements.append(MVMCoreUISplitViewController.main()?.tabBar)
|
currentController = manager
|
||||||
managerController.view.accessibilityElements = accessibilityElements.compactMap { $0 }
|
} else {
|
||||||
} else if let controller = delegateObject?.moleculeDelegate as? UIViewController {
|
accessibilityElements.append(contentsOf: currentController?.view.subviews ?? [])
|
||||||
accessibilityElements.append(controller.navigationController)
|
|
||||||
accessibilityElements.append(contentsOf: controller.view.subviews.reversed())
|
|
||||||
accessibilityElements.append(MVMCoreUISplitViewController.main()?.tabBar)
|
|
||||||
controller.view.accessibilityElements = accessibilityElements.compactMap { $0 }
|
|
||||||
}
|
}
|
||||||
|
accessibilityElements.append(MVMCoreUISplitViewController.main()?.tabBar)
|
||||||
|
currentController?.view.accessibilityElements = accessibilityElements.compactMap { $0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
//MARK: - Rotor Methods
|
//MARK: - Rotor Methods
|
||||||
private func identifyAndPrepareRotors() {
|
private func identifyAndPrepareRotors(_ delegateObject: MVMCoreUIDelegateObject?) {
|
||||||
var rotorElements: [UIAccessibilityCustomRotor] = []
|
var rotorElements: [UIAccessibilityCustomRotor] = []
|
||||||
let currentViewController = ((delegateObj?.moleculeDelegate as? MVMCoreViewManagerViewControllerProtocol)?.manager as? SubNavManagerController) ?? (delegateObj?.moleculeDelegate as? ViewController)
|
let currentViewController = ((delegateObject?.moleculeDelegate as? MVMCoreViewManagerViewControllerProtocol)?.manager as? SubNavManagerController) ?? (delegateObject?.moleculeDelegate as? ViewController)
|
||||||
for type in RotorType.allCases {
|
for type in RotorType.allCases {
|
||||||
if let elements = getTraitMappedElements(currentViewController, type: type),
|
if let elements = getTraitMappedElements(currentViewController, type: type),
|
||||||
let rotor = createRotor(elements, for: type) {
|
let rotor = createRotor(elements, for: type) {
|
||||||
@ -337,14 +273,76 @@ open class AccessibilityHandlerBehavior: PageVisibilityBehavior, PageMoleculeTra
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
var rotorElement = elements[rotorIndex - 1]
|
var rotorElement = elements[rotorIndex - 1]
|
||||||
if let tableView = (self.delegateObj?.moleculeListDelegate as? MoleculeListTemplate)?.tableView,
|
if let tableView = (self.delegate as? MoleculeListTemplate)?.tableView,
|
||||||
let element = rotorElement as? (model: MoleculeModelProtocol, indexPath: IndexPath) { //for List templates
|
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 = MVMCoreUIUtility.findViews(by: MoleculeViewProtocol.self, views: [tableView.cellForRow(at: element.indexPath)].compactMap { $0 }).filter { $0.accessibilityTraits.contains(type.trait) && ($0 as? MoleculeViewModelProtocol)?.moleculeModel?.id == element.model.id }.first as Any
|
rotorElement = MVMCoreUIUtility.findViews(by: MoleculeViewProtocol.self, views: [tableView.cellForRow(at: element.indexPath)].compactMap { $0 }).filter { $0.accessibilityTraits.contains(type.trait) && $0.model?.id == element.model.id }.first as Any
|
||||||
}
|
}
|
||||||
self.rotorIndexes[type] = rotorIndex
|
self.rotorIndexes[type] = rotorIndex
|
||||||
AccessibilityHandler.shared()?.post(notification: .layoutChanged, argument: rotorElement)
|
post(notification: .layoutChanged, argument: rotorElement)
|
||||||
return UIAccessibilityCustomRotorItemResult(targetElement: rotorElement as! NSObjectProtocol, targetRange: nil)
|
return UIAccessibilityCustomRotorItemResult(targetElement: rotorElement as! NSObjectProtocol, targetRange: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc extension AccessibilityHandler {
|
||||||
|
|
||||||
|
// MARK: - Register with Accessibility Handler listeners
|
||||||
|
private func registerForFocusChanges() {
|
||||||
|
//Since focus shifted to other elements cancelling existing focus shift notifications if any
|
||||||
|
NotificationCenter.default.publisher(for: UIAccessibility.elementFocusedNotification)
|
||||||
|
.sink { [weak self] _ in
|
||||||
|
self?.accessibilityOperationQueue.cancelAllOperations()
|
||||||
|
}.store(in: &anyCancellable)
|
||||||
|
}
|
||||||
|
|
||||||
|
func registerForTopNotificationsChanges() {
|
||||||
|
NotificationHandler.shared()?.onNotificationWillShow
|
||||||
|
.sink { [weak self] (_, model) in
|
||||||
|
if self?.previousAccessiblityElement == nil {
|
||||||
|
self?.capturePreviousFocusElement()
|
||||||
|
}
|
||||||
|
}.store(in: &anyCancellable)
|
||||||
|
NotificationHandler.shared()?.onNotificationShown
|
||||||
|
.sink { [weak self] (view, model) in
|
||||||
|
self?.post(notification: .layoutChanged, argument: view)
|
||||||
|
}.store(in: &anyCancellable)
|
||||||
|
NotificationHandler.shared()?.onNotificationWillDismiss
|
||||||
|
.sink { [weak self] (view, model) in
|
||||||
|
self?.post(notification: .announcement, argument: MVMCoreUIUtility.hardcodedString(withKey: "AccTopAlertClosed"), priority: .veryHigh)
|
||||||
|
}.store(in: &anyCancellable)
|
||||||
|
NotificationHandler.shared()?.onNotificationDismissed
|
||||||
|
.sink { [weak self] (view, model) in
|
||||||
|
self?.postAccessbilityToPrevElement()
|
||||||
|
}.store(in: &anyCancellable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Accessibility Handler Behaviour
|
||||||
|
///Accessibility Handler Behaviour to detect page shown and post notification to first interactive element on screen or the pre-defined focused element.
|
||||||
|
open class AccessibilityHandlerBehavior: PageVisibilityBehavior, PageMoleculeTransformationBehavior {
|
||||||
|
|
||||||
|
public let accessibilityHandler: AccessibilityHandler?
|
||||||
|
|
||||||
|
public init(accessibilityHandler: AccessibilityHandler?) {
|
||||||
|
self.accessibilityHandler = accessibilityHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
required public init(model: PageBehaviorModelProtocol, delegateObject: MVMCoreUIDelegateObject?) {
|
||||||
|
accessibilityHandler = AccessibilityHandler.shared() //Protocol Mandatory init method.
|
||||||
|
}
|
||||||
|
|
||||||
|
//MARK: - PageMoleculeTransformationBehavior
|
||||||
|
public func onPageNew(rootMolecules: [MoleculeModelProtocol], _ delegateObject: MVMCoreUIDelegateObject?) {
|
||||||
|
accessibilityHandler?.onPageNew(rootMolecules: rootMolecules, delegateObject)
|
||||||
|
}
|
||||||
|
|
||||||
|
//MARK: - PageVisibiltyBehaviour
|
||||||
|
open func willShowPage(_ delegateObject: MVMCoreUIDelegateObject?) {
|
||||||
|
accessibilityHandler?.willShowPage(delegateObject)
|
||||||
|
}
|
||||||
|
|
||||||
|
open func onPageShown(_ delegateObject: MVMCoreUIDelegateObject?) {
|
||||||
|
accessibilityHandler?.onPageShown(delegateObject)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -418,8 +418,3 @@ extension Toggle {
|
|||||||
|
|
||||||
public func horizontalAlignment() -> UIStackView.Alignment { .trailing }
|
public func horizontalAlignment() -> UIStackView.Alignment { .trailing }
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Toggle: MoleculeViewModelProtocol {
|
|
||||||
|
|
||||||
public var moleculeModel: MoleculeModelProtocol? { model }
|
|
||||||
}
|
|
||||||
|
|||||||
@ -44,7 +44,7 @@ public typealias ActionBlock = () -> ()
|
|||||||
|
|
||||||
public var shouldMaskWhileRecording: Bool = false
|
public var shouldMaskWhileRecording: Bool = false
|
||||||
|
|
||||||
private var model: MoleculeModelProtocol?
|
public var model: MoleculeModelProtocol?
|
||||||
//------------------------------------------------------
|
//------------------------------------------------------
|
||||||
// MARK: - Multi-Action Text
|
// MARK: - Multi-Action Text
|
||||||
//------------------------------------------------------
|
//------------------------------------------------------
|
||||||
@ -1029,8 +1029,3 @@ func validateAttribute(range: NSRange, in string: NSAttributedString, type: Stri
|
|||||||
|
|
||||||
return range
|
return range
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Label: MoleculeViewModelProtocol {
|
|
||||||
|
|
||||||
public var moleculeModel: MoleculeModelProtocol? { model }
|
|
||||||
}
|
|
||||||
|
|||||||
@ -13,8 +13,11 @@ import Foundation
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Properties
|
// MARK: - Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
public var model: MoleculeModelProtocol?
|
||||||
var progressBarModel: ProgressBarModel?
|
public var progressBarModel: ProgressBarModel? {
|
||||||
|
get { model as? ProgressBarModel }
|
||||||
|
set { model = newValue }
|
||||||
|
}
|
||||||
|
|
||||||
var thickness: CGFloat = 8.0 {
|
var thickness: CGFloat = 8.0 {
|
||||||
willSet(newValue) {
|
willSet(newValue) {
|
||||||
|
|||||||
@ -18,7 +18,13 @@ open class Tilelet: VDS.Tilelet, VDSMoleculeViewProtocol{
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Properties
|
// MARK: - Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
public var viewModel: TileletModel!
|
|
||||||
|
public var model: MoleculeModelProtocol?
|
||||||
|
|
||||||
|
public var viewModel: TileletModel! {
|
||||||
|
get { model as? TileletModel }
|
||||||
|
set { model = newValue }
|
||||||
|
}
|
||||||
public var delegateObject: MVMCoreUIDelegateObject?
|
public var delegateObject: MVMCoreUIDelegateObject?
|
||||||
public var additionalData: [AnyHashable: Any]?
|
public var additionalData: [AnyHashable: Any]?
|
||||||
|
|
||||||
|
|||||||
@ -9,7 +9,13 @@ import VDSColorTokens
|
|||||||
|
|
||||||
@objcMembers open class TabBar: UITabBar, MoleculeViewProtocol, TabBarProtocol, UITabBarDelegate {
|
@objcMembers open class TabBar: UITabBar, MoleculeViewProtocol, TabBarProtocol, UITabBarDelegate {
|
||||||
|
|
||||||
public var model: TabBarModel
|
public var model: MoleculeModelProtocol?
|
||||||
|
|
||||||
|
public var tabModel: TabBarModel {
|
||||||
|
get { model as! TabBarModel }
|
||||||
|
set { model = newValue }
|
||||||
|
}
|
||||||
|
|
||||||
public var delegateObject: MVMCoreUIDelegateObject?
|
public var delegateObject: MVMCoreUIDelegateObject?
|
||||||
public let line = Line()
|
public let line = Line()
|
||||||
|
|
||||||
@ -68,8 +74,8 @@ import VDSColorTokens
|
|||||||
|
|
||||||
// MARK: - UITabBarDelegate
|
// MARK: - UITabBarDelegate
|
||||||
public func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
|
public func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
|
||||||
model.selectedTab = item.tag
|
tabModel.selectedTab = item.tag
|
||||||
let action = model.tabs[item.tag].action
|
let action = tabModel.tabs[item.tag].action
|
||||||
Task(priority: .userInitiated) {
|
Task(priority: .userInitiated) {
|
||||||
try await Button.performButtonAction(with: action, button: item, delegateObject: delegateObject, additionalData: nil)
|
try await Button.performButtonAction(with: action, button: item, delegateObject: delegateObject, additionalData: nil)
|
||||||
}
|
}
|
||||||
@ -79,7 +85,7 @@ import VDSColorTokens
|
|||||||
public func highlightTab(at index: Int) {
|
public func highlightTab(at index: Int) {
|
||||||
MVMCoreDispatchUtility.performBlock(onMainThread: {
|
MVMCoreDispatchUtility.performBlock(onMainThread: {
|
||||||
guard let newSelectedItem = self.items?[index] else { return }
|
guard let newSelectedItem = self.items?[index] else { return }
|
||||||
self.model.selectedTab = index
|
self.tabModel.selectedTab = index
|
||||||
self.selectedItem = newSelectedItem
|
self.selectedItem = newSelectedItem
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -92,7 +98,7 @@ import VDSColorTokens
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
public func currentTabIndex() -> Int { model.selectedTab }
|
public func currentTabIndex() -> Int { tabModel.selectedTab }
|
||||||
}
|
}
|
||||||
|
|
||||||
extension UITabBarItem: MFButtonProtocol { }
|
extension UITabBarItem: MFButtonProtocol { }
|
||||||
|
|||||||
@ -12,6 +12,8 @@ import MVMCore.MVMCoreViewProtocol
|
|||||||
|
|
||||||
public protocol MoleculeViewProtocol: UIView, ModelHandlerProtocol {
|
public protocol MoleculeViewProtocol: UIView, ModelHandlerProtocol {
|
||||||
|
|
||||||
|
var model: MoleculeModelProtocol? { get set }
|
||||||
|
|
||||||
/// Initializes the view with the model
|
/// Initializes the view with the model
|
||||||
init(model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?)
|
init(model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?)
|
||||||
|
|
||||||
@ -33,6 +35,11 @@ public protocol MoleculeViewProtocol: UIView, ModelHandlerProtocol {
|
|||||||
|
|
||||||
extension MoleculeViewProtocol {
|
extension MoleculeViewProtocol {
|
||||||
|
|
||||||
|
public var model: MoleculeModelProtocol? {
|
||||||
|
get { nil }
|
||||||
|
set { }
|
||||||
|
}
|
||||||
|
|
||||||
/// Calls set with model
|
/// Calls set with model
|
||||||
public init(model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
public init(model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||||
self.init(frame: .zero)
|
self.init(frame: .zero)
|
||||||
@ -101,15 +108,3 @@ public extension ModelRegistry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public protocol MoleculeViewModelProtocol: UIView {
|
|
||||||
|
|
||||||
var moleculeModel: MoleculeModelProtocol? { get }
|
|
||||||
}
|
|
||||||
|
|
||||||
public extension MoleculeViewModelProtocol {
|
|
||||||
|
|
||||||
var moleculeModel: MoleculeModelProtocol? {
|
|
||||||
get { nil }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -165,8 +165,3 @@ extension Button: AppleGuidelinesProtocol {
|
|||||||
Self.acceptablyOutsideBounds(point: point, bounds: bounds)
|
Self.acceptablyOutsideBounds(point: point, bounds: bounds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Button: MoleculeViewModelProtocol {
|
|
||||||
|
|
||||||
public var moleculeModel: MoleculeModelProtocol? { model }
|
|
||||||
}
|
|
||||||
|
|||||||
@ -83,6 +83,11 @@ import Combine
|
|||||||
}
|
}
|
||||||
|
|
||||||
extension NavigationController: MVMCoreViewManagerProtocol {
|
extension NavigationController: MVMCoreViewManagerProtocol {
|
||||||
|
|
||||||
|
public func getAccessibilityElements() -> [Any]? {
|
||||||
|
nil
|
||||||
|
}
|
||||||
|
|
||||||
public func getCurrentViewController() -> UIViewController? {
|
public func getCurrentViewController() -> UIViewController? {
|
||||||
guard let topViewController = topViewController else { return nil }
|
guard let topViewController = topViewController else { return nil }
|
||||||
return MVMCoreUIUtility.getViewControllerTraversingManagers(topViewController)
|
return MVMCoreUIUtility.getViewControllerTraversingManagers(topViewController)
|
||||||
|
|||||||
@ -249,6 +249,11 @@ public extension MVMCoreUISplitViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extension MVMCoreUISplitViewController: MVMCoreViewManagerProtocol {
|
extension MVMCoreUISplitViewController: MVMCoreViewManagerProtocol {
|
||||||
|
|
||||||
|
public func getAccessibilityElements() -> [Any]? {
|
||||||
|
nil
|
||||||
|
}
|
||||||
|
|
||||||
public func getCurrentViewController() -> UIViewController? {
|
public func getCurrentViewController() -> UIViewController? {
|
||||||
navigationController?.getCurrentViewController()
|
navigationController?.getCurrentViewController()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -307,6 +307,10 @@ open class SubNavManagerController: ViewController, MVMCoreViewManagerProtocol,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc public func getAccessibilityElements() -> [Any]? {
|
||||||
|
[tabs]
|
||||||
|
}
|
||||||
|
|
||||||
open func newDataReceived(in viewController: UIViewController) {
|
open func newDataReceived(in viewController: UIViewController) {
|
||||||
manager?.newDataReceived?(in: viewController)
|
manager?.newDataReceived?(in: viewController)
|
||||||
hideNavigationBarLine(true)
|
hideNavigationBarLine(true)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user