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:
commit
0f276f7822
@ -47,6 +47,8 @@ open class Control: UIControl, ViewProtocol, UserInfoable, Clickable {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
open var shouldUpdateView: Bool = true
|
open var shouldUpdateView: Bool = true
|
||||||
|
|
||||||
|
open var shouldUpdateAccessibility: Bool = true
|
||||||
|
|
||||||
open var userInfo = [String: Primitive]()
|
open var userInfo = [String: Primitive]()
|
||||||
|
|
||||||
open var surface: Surface = .light { didSet { setNeedsUpdate() } }
|
open var surface: Surface = .light { didSet { setNeedsUpdate() } }
|
||||||
|
|||||||
@ -124,6 +124,7 @@ open class SelectorBase: Control, SelectorControlable {
|
|||||||
open override func updateAccessibility() {
|
open override func updateAccessibility() {
|
||||||
super.updateAccessibility()
|
super.updateAccessibility()
|
||||||
accessibilityLabel = "\(Self.self)\(showError ? ", error" : "")"
|
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.
|
/// This will change the state of the Selector and execute the actionBlock if provided.
|
||||||
|
|||||||
@ -147,27 +147,51 @@ open class SelectorItemBase<Selector: SelectorControlable>: Control, Errorable,
|
|||||||
|
|
||||||
open var accessibilityValueText: String?
|
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
|
// MARK: - Overrides
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
/// Executed on initialization for this View.
|
/// Executed on initialization for this View.
|
||||||
open override func initialSetup() {
|
open override func initialSetup() {
|
||||||
super.initialSetup()
|
super.initialSetup()
|
||||||
onClick = { control in
|
onClick = { [weak self] control in
|
||||||
control.toggle()
|
self?.toggle()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations.
|
/// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations.
|
||||||
open override func setup() {
|
open override func setup() {
|
||||||
super.setup()
|
super.setup()
|
||||||
|
|
||||||
selectorView.isAccessibilityElement = false
|
selectorView.isAccessibilityElement = true
|
||||||
isAccessibilityElement = true
|
selectorView.shouldUpdateAccessibility = false
|
||||||
accessibilityTraits = .button
|
|
||||||
addSubview(mainStackView)
|
|
||||||
mainStackView.isUserInteractionEnabled = false
|
|
||||||
|
|
||||||
|
isAccessibilityElement = false
|
||||||
|
addSubview(mainStackView)
|
||||||
|
|
||||||
|
mainStackView.isUserInteractionEnabled = false
|
||||||
mainStackView.addArrangedSubview(selectorStackView)
|
mainStackView.addArrangedSubview(selectorStackView)
|
||||||
mainStackView.addArrangedSubview(errorLabel)
|
mainStackView.addArrangedSubview(errorLabel)
|
||||||
selectorStackView.addArrangedSubview(selectorView)
|
selectorStackView.addArrangedSubview(selectorView)
|
||||||
@ -185,6 +209,7 @@ open class SelectorItemBase<Selector: SelectorControlable>: Control, Errorable,
|
|||||||
open override func updateView() {
|
open override func updateView() {
|
||||||
super.updateView()
|
super.updateView()
|
||||||
updateLabels()
|
updateLabels()
|
||||||
|
selectorView.isUserInteractionEnabled = true
|
||||||
selectorView.showError = showError
|
selectorView.showError = showError
|
||||||
selectorView.isSelected = isSelected
|
selectorView.isSelected = isSelected
|
||||||
selectorView.isHighlighted = isHighlighted
|
selectorView.isHighlighted = isHighlighted
|
||||||
@ -195,10 +220,35 @@ open class SelectorItemBase<Selector: SelectorControlable>: Control, Errorable,
|
|||||||
/// Used to update any Accessibility properties.
|
/// Used to update any Accessibility properties.
|
||||||
open override func updateAccessibility() {
|
open override func updateAccessibility() {
|
||||||
super.updateAccessibility()
|
super.updateAccessibility()
|
||||||
setAccessibilityLabel(for: [selectorView, label, childLabel, errorLabel])
|
selectorView.accessibilityLabel = accessibilityLabelText
|
||||||
|
selectorView.accessibilityHint = !isEnabled ? "" : "Double tap to activate."
|
||||||
accessibilityValue = accessibilityValueText
|
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
|
/// 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? {
|
open override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||||
let labelPoint = convert(point, to: label)
|
let labelPoint = convert(point, to: label)
|
||||||
|
|||||||
@ -46,6 +46,8 @@ open class View: UIView, ViewProtocol, UserInfoable {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
open var shouldUpdateView: Bool = true
|
open var shouldUpdateView: Bool = true
|
||||||
|
|
||||||
|
open var shouldUpdateAccessibility: Bool = true
|
||||||
|
|
||||||
/// Dictionary for keeping information for this Control use only Primitives.
|
/// Dictionary for keeping information for this Control use only Primitives.
|
||||||
open var userInfo = [String: Primitive]()
|
open var userInfo = [String: Primitive]()
|
||||||
|
|
||||||
|
|||||||
@ -51,6 +51,8 @@ open class ButtonBase: UIButton, ViewProtocol, UserInfoable, Clickable {
|
|||||||
/// Key of whether or not updateView() is called in setNeedsUpdate()
|
/// Key of whether or not updateView() is called in setNeedsUpdate()
|
||||||
open var shouldUpdateView: Bool = true
|
open var shouldUpdateView: Bool = true
|
||||||
|
|
||||||
|
open var shouldUpdateAccessibility: Bool = true
|
||||||
|
|
||||||
open var surface: Surface = .light { didSet { setNeedsUpdate() } }
|
open var surface: Surface = .light { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
/// Text that will be used in the titleLabel.
|
/// Text that will be used in the titleLabel.
|
||||||
|
|||||||
@ -108,6 +108,8 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
|
|||||||
/// Key of whether or not updateView() is called in setNeedsUpdate()
|
/// Key of whether or not updateView() is called in setNeedsUpdate()
|
||||||
open var shouldUpdateView: Bool = true
|
open var shouldUpdateView: Bool = true
|
||||||
|
|
||||||
|
open var shouldUpdateAccessibility: Bool = true
|
||||||
|
|
||||||
/// Will determine if a scaled font should be used for the font.
|
/// Will determine if a scaled font should be used for the font.
|
||||||
open var useScaledFont: Bool = false { didSet { setNeedsUpdate() }}
|
open var useScaledFont: Bool = false { didSet { setNeedsUpdate() }}
|
||||||
|
|
||||||
@ -369,7 +371,7 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let accessibilityElements, !accessibilityElements.isEmpty {
|
if let accessibilityElements, !accessibilityElements.isEmpty {
|
||||||
let staticText = UIAccessibilityElement(accessibilityContainer: self)
|
let staticText = AccessibilityActionElement(accessibilityContainer: self)
|
||||||
staticText.accessibilityLabel = text
|
staticText.accessibilityLabel = text
|
||||||
staticText.accessibilityFrameInContainerSpace = bounds
|
staticText.accessibilityFrameInContainerSpace = bounds
|
||||||
|
|
||||||
@ -446,4 +448,9 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
|
|||||||
accessibilityElements?.append(element)
|
accessibilityElements?.append(element)
|
||||||
return element
|
return element
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override func accessibilityActivate() -> Bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -133,6 +133,30 @@ open class RadioBoxItem: Control, Changeable, FormFieldable, Groupable {
|
|||||||
|
|
||||||
open var accessibilityValueText: String?
|
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
|
// MARK: - Configuration Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -171,8 +195,10 @@ open class RadioBoxItem: Control, Changeable, FormFieldable, Groupable {
|
|||||||
open override func setup() {
|
open override func setup() {
|
||||||
super.setup()
|
super.setup()
|
||||||
|
|
||||||
isAccessibilityElement = true
|
isAccessibilityElement = false
|
||||||
accessibilityTraits = .button
|
selectorView.isAccessibilityElement = true
|
||||||
|
selectorView.accessibilityTraits = .button
|
||||||
|
|
||||||
addSubview(selectorView)
|
addSubview(selectorView)
|
||||||
selectorView.isUserInteractionEnabled = false
|
selectorView.isUserInteractionEnabled = false
|
||||||
|
|
||||||
@ -242,12 +268,8 @@ open class RadioBoxItem: Control, Changeable, FormFieldable, Groupable {
|
|||||||
/// Used to update any Accessibility properties.
|
/// Used to update any Accessibility properties.
|
||||||
open override func updateAccessibility() {
|
open override func updateAccessibility() {
|
||||||
super.updateAccessibility()
|
super.updateAccessibility()
|
||||||
setAccessibilityLabel(for: [textLabel, subTextLabel, subTextRightLabel])
|
accessibilityLabel = accessibilityLabelText
|
||||||
if let currentAccessibilityLabel = accessibilityLabel {
|
|
||||||
accessibilityLabel = "Radiobox, \(currentAccessibilityLabel)"
|
|
||||||
} else {
|
|
||||||
accessibilityLabel = "Radiobox"
|
|
||||||
}
|
|
||||||
if let accessibilityValueText {
|
if let accessibilityValueText {
|
||||||
accessibilityValue = strikethrough
|
accessibilityValue = strikethrough
|
||||||
? "\(strikethroughAccessibilityText), \(accessibilityValueText)"
|
? "\(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
|
// MARK: - Private Methods
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -67,6 +67,8 @@ open class TextField: UITextField, ViewProtocol, Errorable {
|
|||||||
/// Key of whether or not updateView() is called in setNeedsUpdate()
|
/// Key of whether or not updateView() is called in setNeedsUpdate()
|
||||||
open var shouldUpdateView: Bool = true
|
open var shouldUpdateView: Bool = true
|
||||||
|
|
||||||
|
open var shouldUpdateAccessibility: Bool = true
|
||||||
|
|
||||||
open var surface: Surface = .light { didSet { setNeedsUpdate() } }
|
open var surface: Surface = .light { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
open var showError: Bool = false { didSet { setNeedsUpdate() } }
|
open var showError: Bool = false { didSet { setNeedsUpdate() } }
|
||||||
|
|||||||
@ -48,6 +48,8 @@ open class TextView: UITextView, ViewProtocol, Errorable {
|
|||||||
/// Key of whether or not updateView() is called in setNeedsUpdate()
|
/// Key of whether or not updateView() is called in setNeedsUpdate()
|
||||||
open var shouldUpdateView: Bool = true
|
open var shouldUpdateView: Bool = true
|
||||||
|
|
||||||
|
open var shouldUpdateAccessibility: Bool = true
|
||||||
|
|
||||||
open var surface: Surface = .light { didSet { setNeedsUpdate() } }
|
open var surface: Surface = .light { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
/// Array of LabelAttributeModel objects used in rendering the text.
|
/// Array of LabelAttributeModel objects used in rendering the text.
|
||||||
|
|||||||
@ -16,6 +16,9 @@ public protocol ViewProtocol: AnyObject, Initable, Resettable, Enabling, Surface
|
|||||||
/// Key of whether or not updateView() is called in setNeedsUpdate()
|
/// Key of whether or not updateView() is called in setNeedsUpdate()
|
||||||
var shouldUpdateView: Bool { get set }
|
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.
|
/// Executed on initialization for this View.
|
||||||
func initialSetup()
|
func initialSetup()
|
||||||
|
|
||||||
@ -30,12 +33,15 @@ public protocol ViewProtocol: AnyObject, Initable, Resettable, Enabling, Surface
|
|||||||
}
|
}
|
||||||
|
|
||||||
extension ViewProtocol {
|
extension ViewProtocol {
|
||||||
|
|
||||||
/// Called when there are changes in a View based off a change events or from local properties.
|
/// Called when there are changes in a View based off a change events or from local properties.
|
||||||
public func setNeedsUpdate() {
|
public func setNeedsUpdate() {
|
||||||
if shouldUpdateView {
|
if shouldUpdateView {
|
||||||
shouldUpdateView = false
|
shouldUpdateView = false
|
||||||
updateView()
|
updateView()
|
||||||
updateAccessibility()
|
if shouldUpdateAccessibility {
|
||||||
|
updateAccessibility()
|
||||||
|
}
|
||||||
shouldUpdateView = true
|
shouldUpdateView = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user