Merge branch 'refactor/selector-accessibility' into mbruce/bugfix

# Conflicts:
#	VDS/BaseClasses/Selector/SelectorItemBase.swift
#	VDS/Components/Label/Label.swift

Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
This commit is contained in:
Matt Bruce 2024-06-19 12:31:27 -05:00
commit 0f276f7822
10 changed files with 144 additions and 19 deletions

View File

@ -47,6 +47,8 @@ open class Control: UIControl, ViewProtocol, UserInfoable, Clickable {
//--------------------------------------------------
open var shouldUpdateView: Bool = true
open var shouldUpdateAccessibility: Bool = true
open var userInfo = [String: Primitive]()
open var surface: Surface = .light { didSet { setNeedsUpdate() } }

View File

@ -124,6 +124,7 @@ open class SelectorBase: Control, SelectorControlable {
open override func updateAccessibility() {
super.updateAccessibility()
accessibilityLabel = "\(Self.self)\(showError ? ", error" : "")"
accessibilityHint = !isEnabled ? "" : "Double tap to open."
}
/// This will change the state of the Selector and execute the actionBlock if provided.

View File

@ -147,27 +147,51 @@ open class SelectorItemBase<Selector: SelectorControlable>: Control, Errorable,
open var accessibilityValueText: String?
open var accessibilityLabelText: String {
var accessibilityLabels = [String]()
accessibilityLabels.append("\(Selector.self)")
if let text = labelText, !text.isEmpty {
accessibilityLabels.append(text)
}
if let text = childText, !text.isEmpty {
accessibilityLabels.append(text)
}
if !isEnabled {
accessibilityLabels.append("dimmed")
}
if let errorText, showError, !errorText.isEmpty {
accessibilityLabels.append("error, \(errorText)")
}
return accessibilityLabels.joined(separator: ", ")
}
//--------------------------------------------------
// MARK: - Overrides
//--------------------------------------------------
/// Executed on initialization for this View.
open override func initialSetup() {
super.initialSetup()
onClick = { control in
control.toggle()
}
onClick = { [weak self] control in
self?.toggle()
}
}
/// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations.
open override func setup() {
super.setup()
selectorView.isAccessibilityElement = false
isAccessibilityElement = true
accessibilityTraits = .button
addSubview(mainStackView)
mainStackView.isUserInteractionEnabled = false
selectorView.isAccessibilityElement = true
selectorView.shouldUpdateAccessibility = false
isAccessibilityElement = false
addSubview(mainStackView)
mainStackView.isUserInteractionEnabled = false
mainStackView.addArrangedSubview(selectorStackView)
mainStackView.addArrangedSubview(errorLabel)
selectorStackView.addArrangedSubview(selectorView)
@ -185,6 +209,7 @@ open class SelectorItemBase<Selector: SelectorControlable>: Control, Errorable,
open override func updateView() {
super.updateView()
updateLabels()
selectorView.isUserInteractionEnabled = true
selectorView.showError = showError
selectorView.isSelected = isSelected
selectorView.isHighlighted = isHighlighted
@ -195,10 +220,35 @@ open class SelectorItemBase<Selector: SelectorControlable>: Control, Errorable,
/// Used to update any Accessibility properties.
open override func updateAccessibility() {
super.updateAccessibility()
setAccessibilityLabel(for: [selectorView, label, childLabel, errorLabel])
selectorView.accessibilityLabel = accessibilityLabelText
selectorView.accessibilityHint = !isEnabled ? "" : "Double tap to activate."
accessibilityValue = accessibilityValueText
}
open override var accessibilityElements: [Any]? {
get {
var elements = [Any]()
elements.append(selectorView)
if let text = labelText, !text.isEmpty {
elements.append(label)
}
if let text = childText, !text.isEmpty {
elements.append(childLabel)
}
if let errorText, showError, !errorText.isEmpty {
elements.append(errorLabel)
}
return elements
}
set {
super.accessibilityElements = newValue
}
}
/// Overriden to take the hit if there is an onClickSubscriber and the view is not a UIControl
open override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let labelPoint = convert(point, to: label)

View File

@ -46,6 +46,8 @@ open class View: UIView, ViewProtocol, UserInfoable {
//--------------------------------------------------
open var shouldUpdateView: Bool = true
open var shouldUpdateAccessibility: Bool = true
/// Dictionary for keeping information for this Control use only Primitives.
open var userInfo = [String: Primitive]()

View File

@ -51,6 +51,8 @@ open class ButtonBase: UIButton, ViewProtocol, UserInfoable, Clickable {
/// Key of whether or not updateView() is called in setNeedsUpdate()
open var shouldUpdateView: Bool = true
open var shouldUpdateAccessibility: Bool = true
open var surface: Surface = .light { didSet { setNeedsUpdate() } }
/// Text that will be used in the titleLabel.

View File

@ -108,6 +108,8 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
/// Key of whether or not updateView() is called in setNeedsUpdate()
open var shouldUpdateView: Bool = true
open var shouldUpdateAccessibility: Bool = true
/// Will determine if a scaled font should be used for the font.
open var useScaledFont: Bool = false { didSet { setNeedsUpdate() }}
@ -369,7 +371,7 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
}
if let accessibilityElements, !accessibilityElements.isEmpty {
let staticText = UIAccessibilityElement(accessibilityContainer: self)
let staticText = AccessibilityActionElement(accessibilityContainer: self)
staticText.accessibilityLabel = text
staticText.accessibilityFrameInContainerSpace = bounds
@ -446,4 +448,9 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
accessibilityElements?.append(element)
return element
}
public override func accessibilityActivate() -> Bool {
return false
}
}

View File

@ -133,6 +133,30 @@ open class RadioBoxItem: Control, Changeable, FormFieldable, Groupable {
open var accessibilityValueText: String?
open var accessibilityLabelText: String {
var accessibilityLabels = [String]()
accessibilityLabels.append("Radiobox")
if let text {
accessibilityLabels.append(text)
}
if let text = subText {
accessibilityLabels.append(text)
}
if let text = subTextRight {
accessibilityLabels.append(text)
}
if !isEnabled {
accessibilityLabels.append("dimmed")
}
return accessibilityLabels.joined(separator: ", ")
}
//--------------------------------------------------
// MARK: - Configuration Properties
//--------------------------------------------------
@ -171,8 +195,10 @@ open class RadioBoxItem: Control, Changeable, FormFieldable, Groupable {
open override func setup() {
super.setup()
isAccessibilityElement = true
accessibilityTraits = .button
isAccessibilityElement = false
selectorView.isAccessibilityElement = true
selectorView.accessibilityTraits = .button
addSubview(selectorView)
selectorView.isUserInteractionEnabled = false
@ -242,12 +268,8 @@ open class RadioBoxItem: Control, Changeable, FormFieldable, Groupable {
/// Used to update any Accessibility properties.
open override func updateAccessibility() {
super.updateAccessibility()
setAccessibilityLabel(for: [textLabel, subTextLabel, subTextRightLabel])
if let currentAccessibilityLabel = accessibilityLabel {
accessibilityLabel = "Radiobox, \(currentAccessibilityLabel)"
} else {
accessibilityLabel = "Radiobox"
}
accessibilityLabel = accessibilityLabelText
if let accessibilityValueText {
accessibilityValue = strikethrough
? "\(strikethroughAccessibilityText), \(accessibilityValueText)"
@ -259,6 +281,35 @@ open class RadioBoxItem: Control, Changeable, FormFieldable, Groupable {
}
}
open override var accessibilityElements: [Any]? {
get {
var items = [Any]()
items.append(selectorView)
let elements = gatherAccessibilityElements(from: selectorView)
let views = elements.compactMap({ $0 as? UIView })
//update accessibilityLabel
selectorView.setAccessibilityLabel(for: views)
//disabled
if !isEnabled {
if let label = selectorView.accessibilityLabel, !label.isEmpty {
selectorView.accessibilityLabel = "\(label), dimmed"
} else {
selectorView.accessibilityLabel = "dimmed"
}
}
//append all children that are accessible
items.append(contentsOf: elements)
return items
}
set {}
}
//--------------------------------------------------
// MARK: - Private Methods
//--------------------------------------------------

View File

@ -67,6 +67,8 @@ open class TextField: UITextField, ViewProtocol, Errorable {
/// Key of whether or not updateView() is called in setNeedsUpdate()
open var shouldUpdateView: Bool = true
open var shouldUpdateAccessibility: Bool = true
open var surface: Surface = .light { didSet { setNeedsUpdate() } }
open var showError: Bool = false { didSet { setNeedsUpdate() } }

View File

@ -48,6 +48,8 @@ open class TextView: UITextView, ViewProtocol, Errorable {
/// Key of whether or not updateView() is called in setNeedsUpdate()
open var shouldUpdateView: Bool = true
open var shouldUpdateAccessibility: Bool = true
open var surface: Surface = .light { didSet { setNeedsUpdate() } }
/// Array of LabelAttributeModel objects used in rendering the text.

View File

@ -16,6 +16,9 @@ public protocol ViewProtocol: AnyObject, Initable, Resettable, Enabling, Surface
/// Key of whether or not updateView() is called in setNeedsUpdate()
var shouldUpdateView: Bool { get set }
/// Key of whether or not updateAccessibility() is called in setNeedsUpdate()
var shouldUpdateAccessibility: Bool { get set }
/// Executed on initialization for this View.
func initialSetup()
@ -30,12 +33,15 @@ public protocol ViewProtocol: AnyObject, Initable, Resettable, Enabling, Surface
}
extension ViewProtocol {
/// Called when there are changes in a View based off a change events or from local properties.
public func setNeedsUpdate() {
if shouldUpdateView {
shouldUpdateView = false
updateView()
updateAccessibility()
if shouldUpdateAccessibility {
updateAccessibility()
}
shouldUpdateView = true
}
}