Merge branch 'refactor/accessibilityBlocks' into 'develop'
added shouldUpdateAccessibility See merge request BPHV_MIPS/vds_ios!261
This commit is contained in:
commit
eeae91e616
@ -172,6 +172,8 @@
|
|||||||
EAF193432C134F3800C68D18 /* TableCellItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 443DBAF92BDA303F0021497E /* TableCellItem.swift */; };
|
EAF193432C134F3800C68D18 /* TableCellItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 443DBAF92BDA303F0021497E /* TableCellItem.swift */; };
|
||||||
EAF1FE9929D4850E00101452 /* Clickable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF1FE9829D4850E00101452 /* Clickable.swift */; };
|
EAF1FE9929D4850E00101452 /* Clickable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF1FE9829D4850E00101452 /* Clickable.swift */; };
|
||||||
EAF1FE9B29DB1A6000101452 /* Changeable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF1FE9A29DB1A6000101452 /* Changeable.swift */; };
|
EAF1FE9B29DB1A6000101452 /* Changeable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF1FE9A29DB1A6000101452 /* Changeable.swift */; };
|
||||||
|
EAF2F4762C231EAA007BFEDC /* AccessibilityActionElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF2F4752C231EAA007BFEDC /* AccessibilityActionElement.swift */; };
|
||||||
|
EAF2F4782C249D72007BFEDC /* AccessibilityUpdatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF2F4772C249D72007BFEDC /* AccessibilityUpdatable.swift */; };
|
||||||
EAF7F0952899861000B287F5 /* CheckboxItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F0932899861000B287F5 /* CheckboxItem.swift */; };
|
EAF7F0952899861000B287F5 /* CheckboxItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F0932899861000B287F5 /* CheckboxItem.swift */; };
|
||||||
EAF7F09A2899B17200B287F5 /* CATransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F0992899B17200B287F5 /* CATransaction.swift */; };
|
EAF7F09A2899B17200B287F5 /* CATransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F0992899B17200B287F5 /* CATransaction.swift */; };
|
||||||
EAF7F0A0289AB7EC00B287F5 /* View.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F09F289AB7EC00B287F5 /* View.swift */; };
|
EAF7F0A0289AB7EC00B287F5 /* View.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F09F289AB7EC00B287F5 /* View.swift */; };
|
||||||
@ -398,6 +400,8 @@
|
|||||||
EAEEECAE2B1FC2BA00531FC2 /* ToggleViewChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = ToggleViewChangeLog.txt; sourceTree = "<group>"; };
|
EAEEECAE2B1FC2BA00531FC2 /* ToggleViewChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = ToggleViewChangeLog.txt; sourceTree = "<group>"; };
|
||||||
EAF1FE9829D4850E00101452 /* Clickable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Clickable.swift; sourceTree = "<group>"; };
|
EAF1FE9829D4850E00101452 /* Clickable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Clickable.swift; sourceTree = "<group>"; };
|
||||||
EAF1FE9A29DB1A6000101452 /* Changeable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Changeable.swift; sourceTree = "<group>"; };
|
EAF1FE9A29DB1A6000101452 /* Changeable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Changeable.swift; sourceTree = "<group>"; };
|
||||||
|
EAF2F4752C231EAA007BFEDC /* AccessibilityActionElement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessibilityActionElement.swift; sourceTree = "<group>"; };
|
||||||
|
EAF2F4772C249D72007BFEDC /* AccessibilityUpdatable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessibilityUpdatable.swift; sourceTree = "<group>"; };
|
||||||
EAF7F0932899861000B287F5 /* CheckboxItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CheckboxItem.swift; sourceTree = "<group>"; };
|
EAF7F0932899861000B287F5 /* CheckboxItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CheckboxItem.swift; sourceTree = "<group>"; };
|
||||||
EAF7F0992899B17200B287F5 /* CATransaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CATransaction.swift; sourceTree = "<group>"; };
|
EAF7F0992899B17200B287F5 /* CATransaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CATransaction.swift; sourceTree = "<group>"; };
|
||||||
EAF7F09F289AB7EC00B287F5 /* View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = View.swift; sourceTree = "<group>"; };
|
EAF7F09F289AB7EC00B287F5 /* View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = View.swift; sourceTree = "<group>"; };
|
||||||
@ -711,6 +715,7 @@
|
|||||||
EA3361AB288B25EC0071C351 /* Protocols */ = {
|
EA3361AB288B25EC0071C351 /* Protocols */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
EAF2F4772C249D72007BFEDC /* AccessibilityUpdatable.swift */,
|
||||||
EA4DB2FC28D3D0CA00103EE3 /* AnyEquatable.swift */,
|
EA4DB2FC28D3D0CA00103EE3 /* AnyEquatable.swift */,
|
||||||
EA297A5629FB0A360031ED56 /* AppleGuidelinesTouchable.swift */,
|
EA297A5629FB0A360031ED56 /* AppleGuidelinesTouchable.swift */,
|
||||||
EAF1FE9A29DB1A6000101452 /* Changeable.swift */,
|
EAF1FE9A29DB1A6000101452 /* Changeable.swift */,
|
||||||
@ -743,6 +748,7 @@
|
|||||||
EA985C1C296CD13600F2FF2E /* BundleManager.swift */,
|
EA985C1C296CD13600F2FF2E /* BundleManager.swift */,
|
||||||
EAF7F0B8289C139800B287F5 /* ColorConfiguration.swift */,
|
EAF7F0B8289C139800B287F5 /* ColorConfiguration.swift */,
|
||||||
EAB5FEF02927F4AA00998C17 /* SelfSizingCollectionView.swift */,
|
EAB5FEF02927F4AA00998C17 /* SelfSizingCollectionView.swift */,
|
||||||
|
EAF2F4752C231EAA007BFEDC /* AccessibilityActionElement.swift */,
|
||||||
);
|
);
|
||||||
path = Classes;
|
path = Classes;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -1206,6 +1212,7 @@
|
|||||||
EAF7F0A6289B0CE000B287F5 /* Resetable.swift in Sources */,
|
EAF7F0A6289B0CE000B287F5 /* Resetable.swift in Sources */,
|
||||||
EA985C2D296F03FE00F2FF2E /* TileletIconModels.swift in Sources */,
|
EA985C2D296F03FE00F2FF2E /* TileletIconModels.swift in Sources */,
|
||||||
EA89200428AECF4B006B9984 /* UITextField+Publisher.swift in Sources */,
|
EA89200428AECF4B006B9984 /* UITextField+Publisher.swift in Sources */,
|
||||||
|
EAF2F4782C249D72007BFEDC /* AccessibilityUpdatable.swift in Sources */,
|
||||||
18A65A022B96E848006602CC /* Breadcrumbs.swift in Sources */,
|
18A65A022B96E848006602CC /* Breadcrumbs.swift in Sources */,
|
||||||
1842B1E12BECE7B70021AFCA /* CalendarHeaderReusableView.swift in Sources */,
|
1842B1E12BECE7B70021AFCA /* CalendarHeaderReusableView.swift in Sources */,
|
||||||
EA78C7962C00CAC200430AD1 /* Groupable.swift in Sources */,
|
EA78C7962C00CAC200430AD1 /* Groupable.swift in Sources */,
|
||||||
@ -1244,6 +1251,7 @@
|
|||||||
EA0D1C412A6AD61C00E5C127 /* Typography+Additional.swift in Sources */,
|
EA0D1C412A6AD61C00E5C127 /* Typography+Additional.swift in Sources */,
|
||||||
EAC925842911C63100091998 /* Colorable.swift in Sources */,
|
EAC925842911C63100091998 /* Colorable.swift in Sources */,
|
||||||
18B463A42BBD3C46005C4528 /* DropdownOptionModel.swift in Sources */,
|
18B463A42BBD3C46005C4528 /* DropdownOptionModel.swift in Sources */,
|
||||||
|
EAF2F4762C231EAA007BFEDC /* AccessibilityActionElement.swift in Sources */,
|
||||||
EAC58BFD2BE935C300BA39FA /* TitleLockupTextColor.swift in Sources */,
|
EAC58BFD2BE935C300BA39FA /* TitleLockupTextColor.swift in Sources */,
|
||||||
EAACB89A2B927108006A3869 /* Valuing.swift in Sources */,
|
EAACB89A2B927108006A3869 /* Valuing.swift in Sources */,
|
||||||
EAE785312BA0A438009428EA /* UIImage+Helper.swift in Sources */,
|
EAE785312BA0A438009428EA /* UIImage+Helper.swift in Sources */,
|
||||||
|
|||||||
@ -119,17 +119,136 @@ open class Control: UIControl, ViewProtocol, UserInfoable, Clickable {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Overrides
|
// MARK: - Overrides
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
/// Implement accessibilityActivate on an element in order to handle the default action.
|
|
||||||
/// - Returns: Based on whether the userInteraction is enabled.
|
|
||||||
override open func accessibilityActivate() -> Bool {
|
|
||||||
// Hold state in case User wanted isAnimated to remain off.
|
|
||||||
guard isUserInteractionEnabled else { return false }
|
|
||||||
sendActions(for: .touchUpInside)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
open override func layoutSubviews() {
|
open override func layoutSubviews() {
|
||||||
super.layoutSubviews()
|
super.layoutSubviews()
|
||||||
setNeedsUpdate()
|
setNeedsUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Accessibility
|
||||||
|
//--------------------------------------------------
|
||||||
|
open var accessibilityAction: ((Control) -> Void)?
|
||||||
|
|
||||||
|
private var _isAccessibilityElement: Bool = false
|
||||||
|
open override var isAccessibilityElement: Bool {
|
||||||
|
get {
|
||||||
|
var block: AXBoolReturnBlock?
|
||||||
|
|
||||||
|
// if #available(iOS 17, *) {
|
||||||
|
// block = isAccessibilityElementBlock
|
||||||
|
// }
|
||||||
|
|
||||||
|
if block == nil {
|
||||||
|
block = bridge_isAccessibilityElementBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
if let block {
|
||||||
|
return block()
|
||||||
|
} else {
|
||||||
|
return _isAccessibilityElement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
_isAccessibilityElement = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var _accessibilityLabel: String?
|
||||||
|
open override var accessibilityLabel: String? {
|
||||||
|
get {
|
||||||
|
var block: AXStringReturnBlock?
|
||||||
|
// if #available(iOS 17, *) {
|
||||||
|
// block = accessibilityLabelBlock
|
||||||
|
// }
|
||||||
|
|
||||||
|
if block == nil {
|
||||||
|
block = bridge_accessibilityLabelBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
if let block {
|
||||||
|
return block()
|
||||||
|
} else {
|
||||||
|
return _accessibilityLabel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
_accessibilityLabel = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var _accessibilityHint: String?
|
||||||
|
open override var accessibilityHint: String? {
|
||||||
|
get {
|
||||||
|
var block: AXStringReturnBlock?
|
||||||
|
// if #available(iOS 17, *) {
|
||||||
|
// block = accessibilityHintBlock
|
||||||
|
// }
|
||||||
|
|
||||||
|
if block == nil {
|
||||||
|
block = bridge_accessibilityHintBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
if let block {
|
||||||
|
return block()
|
||||||
|
} else {
|
||||||
|
return _accessibilityHint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
_accessibilityHint = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var _accessibilityValue: String?
|
||||||
|
open override var accessibilityValue: String? {
|
||||||
|
get {
|
||||||
|
var block: AXStringReturnBlock?
|
||||||
|
|
||||||
|
// if #available(iOS 17, *) {
|
||||||
|
// block = accessibilityHintBlock
|
||||||
|
// }
|
||||||
|
|
||||||
|
if block == nil {
|
||||||
|
block = bridge_accessibilityValueBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
if let block{
|
||||||
|
return block()
|
||||||
|
} else {
|
||||||
|
return _accessibilityValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
_accessibilityValue = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open override func accessibilityActivate() -> Bool {
|
||||||
|
guard isEnabled, isUserInteractionEnabled else { return false }
|
||||||
|
var value = true
|
||||||
|
|
||||||
|
// if #available(iOS 17, *) {
|
||||||
|
// if let block = accessibilityAction {
|
||||||
|
// block(self)
|
||||||
|
// } else if let block = accessibilityActivateBlock {
|
||||||
|
// value = block()
|
||||||
|
//
|
||||||
|
// } else if let block = bridge_accessibilityActivateBlock {
|
||||||
|
// value = block()
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// } else {
|
||||||
|
if let block = accessibilityAction {
|
||||||
|
block(self)
|
||||||
|
|
||||||
|
} else if let block = bridge_accessibilityActivateBlock {
|
||||||
|
value = block()
|
||||||
|
}
|
||||||
|
// }
|
||||||
|
|
||||||
|
sendActions(for: .touchUpInside)
|
||||||
|
return value
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -104,6 +104,16 @@ open class SelectorBase: Control, SelectorControlable {
|
|||||||
onClick = { control in
|
onClick = { control in
|
||||||
control.toggle()
|
control.toggle()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bridge_accessibilityLabelBlock = { [weak self] in
|
||||||
|
guard let self else { return "" }
|
||||||
|
return "\(Self.self)\(showError ? ", error" : "")"
|
||||||
|
}
|
||||||
|
|
||||||
|
bridge_accessibilityHintBlock = { [weak self] in
|
||||||
|
guard let self else { return "" }
|
||||||
|
return !isEnabled ? "" : "Double tap to activate."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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.
|
||||||
@ -120,12 +130,6 @@ open class SelectorBase: Control, SelectorControlable {
|
|||||||
layoutIfNeeded()
|
layoutIfNeeded()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to update any Accessibility properties.ß
|
|
||||||
open override func updateAccessibility() {
|
|
||||||
super.updateAccessibility()
|
|
||||||
accessibilityLabel = "\(Self.self)\(showError ? ", error" : "")"
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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.
|
||||||
open func toggle() { }
|
open func toggle() { }
|
||||||
|
|
||||||
@ -133,4 +137,36 @@ open class SelectorBase: Control, SelectorControlable {
|
|||||||
super.reset()
|
super.reset()
|
||||||
onChange = nil
|
onChange = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open override func accessibilityActivate() -> Bool {
|
||||||
|
guard isEnabled, isUserInteractionEnabled else { return false }
|
||||||
|
guard isEnabled, isUserInteractionEnabled else { return false }
|
||||||
|
var value = true
|
||||||
|
|
||||||
|
// if #available(iOS 17, *) {
|
||||||
|
// if let block = accessibilityAction {
|
||||||
|
// block(self)
|
||||||
|
//
|
||||||
|
// } else if let block = accessibilityActivateBlock {
|
||||||
|
// value = block()
|
||||||
|
//
|
||||||
|
// } else if let block = bridge_accessibilityActivateBlock {
|
||||||
|
// value = block()
|
||||||
|
//
|
||||||
|
// } else {
|
||||||
|
// toggle()
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
if let block = accessibilityAction {
|
||||||
|
block(self)
|
||||||
|
|
||||||
|
} else if let block = bridge_accessibilityActivateBlock {
|
||||||
|
value = block()
|
||||||
|
|
||||||
|
} else {
|
||||||
|
toggle()
|
||||||
|
}
|
||||||
|
// }
|
||||||
|
return value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -70,6 +70,13 @@ open class SelectorGroupBase<SelectorItemType: Groupable>: Control, SelectorGrou
|
|||||||
self?.didSelect(handler)
|
self?.didSelect(handler)
|
||||||
self?.setNeedsUpdate()
|
self?.setNeedsUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
selector.accessibilityAction = { [weak self] handler in
|
||||||
|
guard let handler = handler as? SelectorItemType else { return }
|
||||||
|
self?.didSelect(handler)
|
||||||
|
self?.setNeedsUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
mainStackView.addArrangedSubview(selector)
|
mainStackView.addArrangedSubview(selector)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import Combine
|
|||||||
import VDSCoreTokens
|
import VDSCoreTokens
|
||||||
|
|
||||||
/// Base Class used to build out a SelectorControlable control.
|
/// Base Class used to build out a SelectorControlable control.
|
||||||
open class SelectorItemBase<Selector: SelectorControlable>: Control, Errorable, Changeable, Groupable {
|
open class SelectorItemBase<Selector: SelectorBase>: Control, Errorable, Changeable, Groupable {
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Initializers
|
// MARK: - Initializers
|
||||||
@ -145,7 +145,14 @@ open class SelectorItemBase<Selector: SelectorControlable>: Control, Errorable,
|
|||||||
|
|
||||||
open var hiddenValue: AnyHashable? { didSet { setNeedsUpdate() } }
|
open var hiddenValue: AnyHashable? { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
open var accessibilityValueText: String?
|
open override var accessibilityAction: ((Control) -> Void)? {
|
||||||
|
didSet {
|
||||||
|
selectorView.accessibilityAction = { [weak self] selectorItemBase in
|
||||||
|
guard let self else { return }
|
||||||
|
accessibilityAction?(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Overrides
|
// MARK: - Overrides
|
||||||
@ -153,21 +160,61 @@ open class SelectorItemBase<Selector: SelectorControlable>: Control, Errorable,
|
|||||||
/// 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()
|
guard let self, isEnabled else { return }
|
||||||
|
toggle()
|
||||||
|
}
|
||||||
|
|
||||||
|
selectorView.accessibilityAction = { [weak self] _ in
|
||||||
|
guard let self, isEnabled else { return }
|
||||||
|
toggle()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
selectorView.bridge_accessibilityLabelBlock = { [weak self ] in
|
||||||
|
guard let self else { return "" }
|
||||||
|
var accessibilityLabels = [String]()
|
||||||
|
|
||||||
|
if isSelected {
|
||||||
|
accessibilityLabels.append("selected")
|
||||||
|
}
|
||||||
|
|
||||||
|
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: ", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
selectorView.bridge_accessibilityHintBlock = { [weak self] in
|
||||||
|
guard let self else { return "" }
|
||||||
|
return !isEnabled ? "" : "Double tap to activate."
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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
|
isAccessibilityElement = false
|
||||||
accessibilityTraits = .button
|
|
||||||
addSubview(mainStackView)
|
addSubview(mainStackView)
|
||||||
mainStackView.isUserInteractionEnabled = false
|
|
||||||
|
|
||||||
|
mainStackView.isUserInteractionEnabled = false
|
||||||
mainStackView.addArrangedSubview(selectorStackView)
|
mainStackView.addArrangedSubview(selectorStackView)
|
||||||
mainStackView.addArrangedSubview(errorLabel)
|
mainStackView.addArrangedSubview(errorLabel)
|
||||||
selectorStackView.addArrangedSubview(selectorView)
|
selectorStackView.addArrangedSubview(selectorView)
|
||||||
@ -192,11 +239,44 @@ open class SelectorItemBase<Selector: SelectorControlable>: Control, Errorable,
|
|||||||
selectorView.surface = surface
|
selectorView.surface = surface
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to update any Accessibility properties.
|
open override var accessibilityElements: [Any]? {
|
||||||
open override func updateAccessibility() {
|
get {
|
||||||
super.updateAccessibility()
|
var elements = [Any]()
|
||||||
setAccessibilityLabel(for: [selectorView, label, childLabel, errorLabel])
|
|
||||||
accessibilityValue = accessibilityValueText
|
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? {
|
||||||
|
guard isEnabled else { return super.hitTest(point, with: event) }
|
||||||
|
|
||||||
|
let labelPoint = convert(point, to: label)
|
||||||
|
let childLabelPoint = convert(point, to: childLabel)
|
||||||
|
if label.isAction(for: labelPoint) {
|
||||||
|
return label
|
||||||
|
} else if childLabel.isAction(for: childLabelPoint) {
|
||||||
|
return childLabel
|
||||||
|
} else {
|
||||||
|
guard !UIAccessibility.isVoiceOverRunning else { return nil }
|
||||||
|
return super.hitTest(point, with: event)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resets to default settings.
|
/// Resets to default settings.
|
||||||
@ -290,4 +370,34 @@ open class SelectorItemBase<Selector: SelectorControlable>: Control, Errorable,
|
|||||||
/// This will change to state of the Selector.
|
/// This will change to state of the Selector.
|
||||||
open func toggle() {}
|
open func toggle() {}
|
||||||
|
|
||||||
|
open override func accessibilityActivate() -> Bool {
|
||||||
|
guard isEnabled, isUserInteractionEnabled else { return false }
|
||||||
|
var value = true
|
||||||
|
|
||||||
|
// if #available(iOS 17, *) {
|
||||||
|
// if let block = accessibilityAction {
|
||||||
|
// block(self)
|
||||||
|
//
|
||||||
|
// } else if let block = accessibilityActivateBlock {
|
||||||
|
// value = block()
|
||||||
|
//
|
||||||
|
// } else if let block = bridge_accessibilityActivateBlock {
|
||||||
|
// value = block()
|
||||||
|
//
|
||||||
|
// } else {
|
||||||
|
// toggle()
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
if let block = accessibilityAction {
|
||||||
|
block(self)
|
||||||
|
|
||||||
|
} else if let block = bridge_accessibilityActivateBlock {
|
||||||
|
value = block()
|
||||||
|
|
||||||
|
} else {
|
||||||
|
toggle()
|
||||||
|
}
|
||||||
|
// }
|
||||||
|
return value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -91,4 +91,136 @@ open class View: UIView, ViewProtocol, UserInfoable {
|
|||||||
setNeedsUpdate()
|
setNeedsUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Accessibility
|
||||||
|
//--------------------------------------------------
|
||||||
|
open var accessibilityAction: ((View) -> Void)?
|
||||||
|
|
||||||
|
private var _isAccessibilityElement: Bool = false
|
||||||
|
open override var isAccessibilityElement: Bool {
|
||||||
|
get {
|
||||||
|
var block: AXBoolReturnBlock?
|
||||||
|
|
||||||
|
// if #available(iOS 17, *) {
|
||||||
|
// block = isAccessibilityElementBlock
|
||||||
|
// }
|
||||||
|
|
||||||
|
if block == nil {
|
||||||
|
block = bridge_isAccessibilityElementBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
if let block {
|
||||||
|
return block()
|
||||||
|
} else {
|
||||||
|
return _isAccessibilityElement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
_isAccessibilityElement = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var _accessibilityLabel: String?
|
||||||
|
open override var accessibilityLabel: String? {
|
||||||
|
get {
|
||||||
|
var block: AXStringReturnBlock?
|
||||||
|
// if #available(iOS 17, *) {
|
||||||
|
// block = accessibilityLabelBlock
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
if block == nil {
|
||||||
|
block = bridge_accessibilityLabelBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
if let block {
|
||||||
|
return block()
|
||||||
|
} else {
|
||||||
|
return _accessibilityLabel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
_accessibilityLabel = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var _accessibilityHint: String?
|
||||||
|
open override var accessibilityHint: String? {
|
||||||
|
get {
|
||||||
|
var block: AXStringReturnBlock?
|
||||||
|
// if #available(iOS 17, *) {
|
||||||
|
// block = accessibilityHintBlock
|
||||||
|
// }
|
||||||
|
|
||||||
|
if block == nil {
|
||||||
|
block = bridge_accessibilityHintBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
if let block {
|
||||||
|
return block()
|
||||||
|
} else {
|
||||||
|
return _accessibilityHint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
_accessibilityHint = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var _accessibilityValue: String?
|
||||||
|
open override var accessibilityValue: String? {
|
||||||
|
get {
|
||||||
|
var block: AXStringReturnBlock?
|
||||||
|
|
||||||
|
// if #available(iOS 17, *) {
|
||||||
|
// block = accessibilityHintBlock
|
||||||
|
// }
|
||||||
|
|
||||||
|
if block == nil {
|
||||||
|
block = bridge_accessibilityValueBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
if let block{
|
||||||
|
return block()
|
||||||
|
} else {
|
||||||
|
return _accessibilityValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
_accessibilityValue = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open override func accessibilityActivate() -> Bool {
|
||||||
|
guard isEnabled, isUserInteractionEnabled else { return false }
|
||||||
|
|
||||||
|
// if #available(iOS 17, *) {
|
||||||
|
// if let block = accessibilityAction {
|
||||||
|
// block(self)
|
||||||
|
// return true
|
||||||
|
// } else if let block = accessibilityActivateBlock {
|
||||||
|
// return block()
|
||||||
|
//
|
||||||
|
// } else if let block = bridge_accessibilityActivateBlock {
|
||||||
|
// return block()
|
||||||
|
//
|
||||||
|
// } else {
|
||||||
|
// return true
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// } else {
|
||||||
|
if let block = accessibilityAction {
|
||||||
|
block(self)
|
||||||
|
return true
|
||||||
|
|
||||||
|
} else if let block = bridge_accessibilityActivateBlock {
|
||||||
|
return block()
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return super.accessibilityActivate()
|
||||||
|
|
||||||
|
}
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
21
VDS/Classes/AccessibilityActionElement.swift
Normal file
21
VDS/Classes/AccessibilityActionElement.swift
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
//
|
||||||
|
// AccessibilityActionElement.swift
|
||||||
|
// VDS
|
||||||
|
//
|
||||||
|
// Created by Matt Bruce on 6/19/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
/// Custom UIAccessibilityElement that allows you to set the default action used in accessibilityActivate.
|
||||||
|
public class AccessibilityActionElement: UIAccessibilityElement {
|
||||||
|
public var accessibilityAction: AXVoidReturnBlock?
|
||||||
|
|
||||||
|
public override func accessibilityActivate() -> Bool {
|
||||||
|
guard let accessibilityAction else { return super.accessibilityActivate() }
|
||||||
|
accessibilityAction()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -147,6 +147,11 @@ open class Badge: View {
|
|||||||
label.widthGreaterThanEqualTo(constant: minWidth)
|
label.widthGreaterThanEqualTo(constant: minWidth)
|
||||||
maxWidthConstraint = label.widthLessThanEqualTo(constant: 0).with { $0.isActive = false }
|
maxWidthConstraint = label.widthLessThanEqualTo(constant: 0).with { $0.isActive = false }
|
||||||
clipsToBounds = true
|
clipsToBounds = true
|
||||||
|
|
||||||
|
bridge_accessibilityLabelBlock = { [weak self] in
|
||||||
|
guard let self else { return "" }
|
||||||
|
return text
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resets to default settings.
|
/// Resets to default settings.
|
||||||
@ -179,10 +184,4 @@ open class Badge: View {
|
|||||||
label.surface = surface
|
label.surface = surface
|
||||||
label.isEnabled = isEnabled
|
label.isEnabled = isEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func updateAccessibility() {
|
|
||||||
super.updateAccessibility()
|
|
||||||
|
|
||||||
accessibilityLabel = text
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -292,6 +292,16 @@ open class BadgeIndicator: View {
|
|||||||
label.centerYAnchor.constraint(equalTo: badgeView.centerYAnchor).isActive = true
|
label.centerYAnchor.constraint(equalTo: badgeView.centerYAnchor).isActive = true
|
||||||
labelContraints.isActive = true
|
labelContraints.isActive = true
|
||||||
|
|
||||||
|
bridge_accessibilityLabelBlock = { [weak self] in
|
||||||
|
guard let self else { return "" }
|
||||||
|
if let accessibilityText {
|
||||||
|
return kind == .numbered ? label.text + " " + accessibilityText : accessibilityText
|
||||||
|
} else if kind == .numbered {
|
||||||
|
return label.text
|
||||||
|
} else {
|
||||||
|
return "Simple"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resets to default settings.
|
/// Resets to default settings.
|
||||||
@ -347,17 +357,6 @@ open class BadgeIndicator: View {
|
|||||||
setNeedsLayout()
|
setNeedsLayout()
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func updateAccessibility() {
|
|
||||||
super.updateAccessibility()
|
|
||||||
if let accessibilityText {
|
|
||||||
accessibilityLabel = kind == .numbered ? label.text + " " + accessibilityText : accessibilityText
|
|
||||||
} else if kind == .numbered {
|
|
||||||
accessibilityLabel = label.text
|
|
||||||
} else {
|
|
||||||
accessibilityLabel = "Simple"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
open override func layoutSubviews() {
|
open override func layoutSubviews() {
|
||||||
super.layoutSubviews()
|
super.layoutSubviews()
|
||||||
|
|
||||||
|
|||||||
@ -82,6 +82,15 @@ open class BreadcrumbItem: ButtonBase {
|
|||||||
isAccessibilityElement = true
|
isAccessibilityElement = true
|
||||||
accessibilityTraits = .link
|
accessibilityTraits = .link
|
||||||
|
|
||||||
|
bridge_accessibilityHintBlock = { [weak self] in
|
||||||
|
guard let self else { return "" }
|
||||||
|
return !isEnabled ? "" : "Double tap to open."
|
||||||
|
}
|
||||||
|
|
||||||
|
bridge_accessibilityLabelBlock = { [weak self] in
|
||||||
|
guard let self else { return "" }
|
||||||
|
return text
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to make changes to the View based off a change events or from local properties.
|
/// Used to make changes to the View based off a change events or from local properties.
|
||||||
@ -134,10 +143,4 @@ open class BreadcrumbItem: ButtonBase {
|
|||||||
setNeedsUpdate()
|
setNeedsUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to update any Accessibility properties.
|
|
||||||
open override func updateAccessibility() {
|
|
||||||
super.updateAccessibility()
|
|
||||||
accessibilityLabel = text
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -172,6 +172,133 @@ open class ButtonBase: UIButton, ViewProtocol, UserInfoable, Clickable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Accessibility
|
||||||
|
//--------------------------------------------------
|
||||||
|
open var accessibilityAction: ((ButtonBase) -> Void)?
|
||||||
|
|
||||||
|
private var _isAccessibilityElement: Bool = false
|
||||||
|
open override var isAccessibilityElement: Bool {
|
||||||
|
get {
|
||||||
|
var block: AXBoolReturnBlock?
|
||||||
|
|
||||||
|
// if #available(iOS 17, *) {
|
||||||
|
// block = isAccessibilityElementBlock
|
||||||
|
// }
|
||||||
|
|
||||||
|
if block == nil {
|
||||||
|
block = bridge_isAccessibilityElementBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
if let block {
|
||||||
|
return block()
|
||||||
|
} else {
|
||||||
|
return _isAccessibilityElement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
_isAccessibilityElement = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var _accessibilityLabel: String?
|
||||||
|
open override var accessibilityLabel: String? {
|
||||||
|
get {
|
||||||
|
var block: AXStringReturnBlock?
|
||||||
|
// if #available(iOS 17, *) {
|
||||||
|
// block = accessibilityLabelBlock
|
||||||
|
// }
|
||||||
|
|
||||||
|
if block == nil {
|
||||||
|
block = bridge_accessibilityLabelBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
if let block {
|
||||||
|
return block()
|
||||||
|
} else {
|
||||||
|
return _accessibilityLabel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
_accessibilityLabel = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var _accessibilityHint: String?
|
||||||
|
open override var accessibilityHint: String? {
|
||||||
|
get {
|
||||||
|
var block: AXStringReturnBlock?
|
||||||
|
// if #available(iOS 17, *) {
|
||||||
|
// block = accessibilityHintBlock
|
||||||
|
// }
|
||||||
|
|
||||||
|
if block == nil {
|
||||||
|
block = bridge_accessibilityHintBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
if let block {
|
||||||
|
return block()
|
||||||
|
} else {
|
||||||
|
return _accessibilityHint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
_accessibilityHint = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var _accessibilityValue: String?
|
||||||
|
open override var accessibilityValue: String? {
|
||||||
|
get {
|
||||||
|
var block: AXStringReturnBlock?
|
||||||
|
|
||||||
|
// if #available(iOS 17, *) {
|
||||||
|
// block = accessibilityHintBlock
|
||||||
|
// }
|
||||||
|
|
||||||
|
if block == nil {
|
||||||
|
block = bridge_accessibilityValueBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
if let block{
|
||||||
|
return block()
|
||||||
|
} else {
|
||||||
|
return _accessibilityValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
_accessibilityValue = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open override func accessibilityActivate() -> Bool {
|
||||||
|
guard isEnabled, isUserInteractionEnabled else { return false }
|
||||||
|
var value = true
|
||||||
|
|
||||||
|
// if #available(iOS 17, *) {
|
||||||
|
// if let block = accessibilityAction {
|
||||||
|
// block(self)
|
||||||
|
// } else if let block = accessibilityActivateBlock {
|
||||||
|
// value = block()
|
||||||
|
//
|
||||||
|
// } else if let block = bridge_accessibilityActivateBlock {
|
||||||
|
// value = block()
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// } else {
|
||||||
|
if let block = accessibilityAction {
|
||||||
|
block(self)
|
||||||
|
|
||||||
|
} else if let block = bridge_accessibilityActivateBlock {
|
||||||
|
value = block()
|
||||||
|
}
|
||||||
|
// }
|
||||||
|
|
||||||
|
sendActions(for: .touchUpInside)
|
||||||
|
return value
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: AppleGuidelinesTouchable
|
// MARK: AppleGuidelinesTouchable
|
||||||
|
|||||||
@ -105,6 +105,12 @@ open class TextLink: ButtonBase {
|
|||||||
lineHeightConstraint = line.height(constant: 1)
|
lineHeightConstraint = line.height(constant: 1)
|
||||||
lineHeightConstraint?.isActive = true
|
lineHeightConstraint?.isActive = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bridge_accessibilityHintBlock = { [weak self] in
|
||||||
|
guard let self else { return "" }
|
||||||
|
return !isEnabled ? "" : "Double tap to open."
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to make changes to the View based off a change events or from local properties.
|
/// Used to make changes to the View based off a change events or from local properties.
|
||||||
|
|||||||
@ -86,6 +86,12 @@ open class TextLinkCaret: ButtonBase {
|
|||||||
accessibilityTraits = .link
|
accessibilityTraits = .link
|
||||||
titleLabel?.numberOfLines = 0
|
titleLabel?.numberOfLines = 0
|
||||||
titleLabel?.lineBreakMode = .byWordWrapping
|
titleLabel?.lineBreakMode = .byWordWrapping
|
||||||
|
|
||||||
|
bridge_accessibilityHintBlock = { [weak self] in
|
||||||
|
guard let self else { return "" }
|
||||||
|
return !isEnabled ? "" : "Double tap to open."
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to make changes to the View based off a change events or from local properties.
|
/// Used to make changes to the View based off a change events or from local properties.
|
||||||
|
|||||||
@ -63,6 +63,8 @@ open class Checkbox: SelectorBase {
|
|||||||
|
|
||||||
/// 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.
|
||||||
open override func toggle() {
|
open override func toggle() {
|
||||||
|
guard isEnabled else { return }
|
||||||
|
|
||||||
//removed error
|
//removed error
|
||||||
if showError && isSelected == false {
|
if showError && isSelected == false {
|
||||||
showError.toggle()
|
showError.toggle()
|
||||||
|
|||||||
@ -47,8 +47,6 @@ open class CheckboxGroup: SelectorGroupBase<CheckboxItem>, SelectorGroupMultiSel
|
|||||||
$0.surface = model.surface
|
$0.surface = model.surface
|
||||||
$0.inputId = model.inputId
|
$0.inputId = model.inputId
|
||||||
$0.hiddenValue = model.value
|
$0.hiddenValue = model.value
|
||||||
$0.accessibilityLabel = model.accessibileText
|
|
||||||
$0.accessibilityValueText = "item \(index+1) of \(selectorModels.count)"
|
|
||||||
$0.labelText = model.labelText
|
$0.labelText = model.labelText
|
||||||
$0.labelTextAttributes = model.labelTextAttributes
|
$0.labelTextAttributes = model.labelTextAttributes
|
||||||
$0.childText = model.childText
|
$0.childText = model.childText
|
||||||
@ -56,6 +54,7 @@ open class CheckboxGroup: SelectorGroupBase<CheckboxItem>, SelectorGroupMultiSel
|
|||||||
$0.isSelected = model.selected
|
$0.isSelected = model.selected
|
||||||
$0.errorText = model.errorText
|
$0.errorText = model.errorText
|
||||||
$0.showError = model.showError
|
$0.showError = model.showError
|
||||||
|
$0.selectorView.bridge_accessibilityValueBlock = { "item \(index+1) of \(selectorModels.count)" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -38,6 +38,8 @@ open class CheckboxItem: SelectorItemBase<Checkbox> {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
/// 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.
|
||||||
open override func toggle() {
|
open override func toggle() {
|
||||||
|
guard isEnabled else { return }
|
||||||
|
|
||||||
//removed error
|
//removed error
|
||||||
if showError && isSelected == false {
|
if showError && isSelected == false {
|
||||||
showError.toggle()
|
showError.toggle()
|
||||||
|
|||||||
@ -132,6 +132,7 @@ open class DropdownSelect: EntryFieldBase {
|
|||||||
/// 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()
|
||||||
|
accessibilityHintText = "has popup, Double tap to open."
|
||||||
|
|
||||||
inlineDisplayLabel.isAccessibilityElement = true
|
inlineDisplayLabel.isAccessibilityElement = true
|
||||||
|
|
||||||
|
|||||||
@ -464,16 +464,18 @@ open class ButtonIcon: Control, Changeable {
|
|||||||
setNeedsLayout()
|
setNeedsLayout()
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func updateAccessibility() {
|
open override var accessibilityElements: [Any]? {
|
||||||
super.updateAccessibility()
|
get {
|
||||||
var elements = [Any]()
|
var elements = [Any]()
|
||||||
if iconName != nil {
|
if iconName != nil {
|
||||||
elements.append(icon)
|
elements.append(icon)
|
||||||
|
}
|
||||||
|
if badgeIndicatorModel != nil && showBadgeIndicator {
|
||||||
|
elements.append(badgeIndicator)
|
||||||
|
}
|
||||||
|
return elements.count > 0 ? elements : nil
|
||||||
}
|
}
|
||||||
if badgeIndicatorModel != nil && showBadgeIndicator {
|
set { }
|
||||||
elements.append(badgeIndicator)
|
|
||||||
}
|
|
||||||
accessibilityElements = elements.count > 0 ? elements : nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func layoutSubviews() {
|
open override func layoutSubviews() {
|
||||||
|
|||||||
@ -94,6 +94,12 @@ open class Icon: View {
|
|||||||
|
|
||||||
isAccessibilityElement = true
|
isAccessibilityElement = true
|
||||||
accessibilityTraits = .image
|
accessibilityTraits = .image
|
||||||
|
|
||||||
|
bridge_accessibilityLabelBlock = { [weak self] in
|
||||||
|
guard let self else { return "" }
|
||||||
|
return name?.rawValue ?? "icon"
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to make changes to the View based off a change events or from local properties.
|
/// Used to make changes to the View based off a change events or from local properties.
|
||||||
@ -119,11 +125,6 @@ open class Icon: View {
|
|||||||
color = VDSColor.paletteBlack
|
color = VDSColor.paletteBlack
|
||||||
imageView.image = nil
|
imageView.image = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func updateAccessibility() {
|
|
||||||
super.updateAccessibility()
|
|
||||||
accessibilityLabel = name?.rawValue ?? "icon"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension UIImage {
|
extension UIImage {
|
||||||
|
|||||||
@ -91,16 +91,14 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
|
|||||||
private struct LabelAction {
|
private struct LabelAction {
|
||||||
var range: NSRange
|
var range: NSRange
|
||||||
var action: PassthroughSubject<Void, Never>
|
var action: PassthroughSubject<Void, Never>
|
||||||
var accessibilityId: Int = 0
|
var frame: CGRect = .zero
|
||||||
|
|
||||||
func performAction() {
|
func performAction() {
|
||||||
action.send()
|
action.send()
|
||||||
}
|
}
|
||||||
|
|
||||||
init(range: NSRange, action: PassthroughSubject<Void, Never>, accessibilityID: Int = 0) {
|
init(range: NSRange, action: PassthroughSubject<Void, Never>) {
|
||||||
self.range = range
|
self.range = range
|
||||||
self.action = action
|
self.action = action
|
||||||
self.accessibilityId = accessibilityID
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,7 +213,12 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open func setup() {}
|
open func setup() {
|
||||||
|
bridge_accessibilityLabelBlock = { [weak self] in
|
||||||
|
guard let self else { return "" }
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
open func reset() {
|
open func reset() {
|
||||||
shouldUpdateView = false
|
shouldUpdateView = false
|
||||||
@ -242,7 +245,6 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
open func updateAccessibility() {
|
open func updateAccessibility() {
|
||||||
accessibilityLabel = text
|
|
||||||
if isEnabled {
|
if isEnabled {
|
||||||
accessibilityTraits.remove(.notEnabled)
|
accessibilityTraits.remove(.notEnabled)
|
||||||
} else {
|
} else {
|
||||||
@ -264,23 +266,6 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
|
|||||||
applyActions()
|
applyActions()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Addig custom accessibillty actions from the collection of attributes.
|
|
||||||
open override func accessibilityActivate() -> Bool {
|
|
||||||
|
|
||||||
guard let accessibleActions = accessibilityCustomActions else { return false }
|
|
||||||
|
|
||||||
for actionable in actions {
|
|
||||||
for action in accessibleActions {
|
|
||||||
if action.hash == actionable.accessibilityId {
|
|
||||||
actionable.performAction()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Private Methods
|
// MARK: - Private Methods
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -373,15 +358,27 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
|
|||||||
//see if the attribute is Actionable
|
//see if the attribute is Actionable
|
||||||
if let actionable = attribute as? any ActionLabelAttributeModel, mutableAttributedString.isValid(range: actionable.range) {
|
if let actionable = attribute as? any ActionLabelAttributeModel, mutableAttributedString.isValid(range: actionable.range) {
|
||||||
//create a accessibleAction
|
//create a accessibleAction
|
||||||
let customAccessibilityAction = customAccessibilityAction(text: mutableAttributedString.string, range: actionable.range, accessibleText: actionable.accessibleText)
|
let customAccessibilityAction = customAccessibilityElement(text: mutableAttributedString.string,
|
||||||
|
range: actionable.range,
|
||||||
|
accessibleText: actionable.accessibleText)
|
||||||
|
|
||||||
|
// creat the action
|
||||||
|
let labelAction = LabelAction(range: actionable.range, action: actionable.action)
|
||||||
|
|
||||||
|
// set the action of the accessibilityElement
|
||||||
|
customAccessibilityAction?.accessibilityAction = { [weak self] in
|
||||||
|
guard let self, isEnabled else { return }
|
||||||
|
labelAction.performAction()
|
||||||
|
}
|
||||||
|
|
||||||
//create a wrapper for the attributes range, block and
|
//create a wrapper for the attributes range, block and
|
||||||
actions.append(LabelAction(range: actionable.range, action: actionable.action, accessibilityID: customAccessibilityAction?.hashValue ?? -1))
|
actions.append(labelAction)
|
||||||
|
isUserInteractionEnabled = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
@ -395,14 +392,40 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
|
|||||||
@objc private func textLinkTapped(_ gesture: UITapGestureRecognizer) {
|
@objc private func textLinkTapped(_ gesture: UITapGestureRecognizer) {
|
||||||
for actionable in actions {
|
for actionable in actions {
|
||||||
// This determines if we tapped on the desired range of text.
|
// This determines if we tapped on the desired range of text.
|
||||||
if gesture.didTapActionInLabel(self, inRange: actionable.range) {
|
let location = gesture.location(in: self)
|
||||||
|
if didTapActionInLabel(location, inRange: actionable.range) {
|
||||||
actionable.performAction()
|
actionable.performAction()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func customAccessibilityAction(text: String?, range: NSRange, accessibleText: String? = nil) -> UIAccessibilityCustomAction? {
|
public func isAction(for location: CGPoint) -> Bool {
|
||||||
|
for actionable in actions {
|
||||||
|
if didTapActionInLabel(location, inRange: actionable.range) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
private func didTapActionInLabel(_ location: CGPoint, inRange targetRange: NSRange) -> Bool {
|
||||||
|
|
||||||
|
guard let attributedText else { return false }
|
||||||
|
let layoutManager = NSLayoutManager()
|
||||||
|
let textContainer = NSTextContainer(size: bounds.size)
|
||||||
|
let textStorage = NSTextStorage(attributedString: attributedText)
|
||||||
|
layoutManager.addTextContainer(textContainer)
|
||||||
|
textStorage.addLayoutManager(layoutManager)
|
||||||
|
|
||||||
|
let characterIndex = layoutManager.characterIndex(for: location, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
|
||||||
|
|
||||||
|
guard let _ = attributedText.attribute(NSAttributedString.Key.action, at: characterIndex, effectiveRange: nil) as? String, characterIndex < attributedText.length else { return false }
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private func customAccessibilityElement(text: String?, range: NSRange, accessibleText: String? = nil) -> AccessibilityActionElement? {
|
||||||
|
|
||||||
guard let text = text, let attributedText else { return nil }
|
guard let text = text, let attributedText else { return nil }
|
||||||
|
|
||||||
@ -423,31 +446,147 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
|
|||||||
let substringBounds = layoutManager.boundingRect(forGlyphRange: glyphRange, in: textContainer)
|
let substringBounds = layoutManager.boundingRect(forGlyphRange: glyphRange, in: textContainer)
|
||||||
|
|
||||||
// Create custom accessibility element
|
// Create custom accessibility element
|
||||||
let element = UIAccessibilityElement(accessibilityContainer: self)
|
let element = AccessibilityActionElement(accessibilityContainer: self)
|
||||||
element.accessibilityLabel = actionText
|
element.accessibilityLabel = actionText
|
||||||
element.accessibilityTraits = .link
|
element.accessibilityTraits = .link
|
||||||
|
element.accessibilityHint = "Double tap to open"
|
||||||
element.accessibilityFrameInContainerSpace = substringBounds
|
element.accessibilityFrameInContainerSpace = substringBounds
|
||||||
|
|
||||||
//TODO: accessibilityHint for Label
|
|
||||||
// element.accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "swipe_to_select_with_action_hint")
|
|
||||||
|
|
||||||
accessibilityElements = (accessibilityElements ?? []).compactMap{$0 as? UIAccessibilityElement}.filter { $0.accessibilityLabel != actionText }
|
accessibilityElements = (accessibilityElements ?? []).compactMap{$0 as? UIAccessibilityElement}.filter { $0.accessibilityLabel != actionText }
|
||||||
accessibilityElements?.append(element)
|
accessibilityElements?.append(element)
|
||||||
|
return element
|
||||||
let accessibleAction = UIAccessibilityCustomAction(name: actionText, target: self, selector: #selector(accessibilityCustomAction(_:)))
|
|
||||||
accessibilityCustomActions?.append(accessibleAction)
|
|
||||||
return accessibleAction
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func accessibilityCustomAction(_ action: UIAccessibilityCustomAction) {
|
|
||||||
|
|
||||||
for actionable in actions {
|
//--------------------------------------------------
|
||||||
if action.hash == actionable.accessibilityId {
|
// MARK: - Accessibility
|
||||||
actionable.performAction()
|
//--------------------------------------------------
|
||||||
return
|
open var accessibilityAction: ((Label) -> Void)?
|
||||||
|
|
||||||
|
private var _isAccessibilityElement: Bool = false
|
||||||
|
open override var isAccessibilityElement: Bool {
|
||||||
|
get {
|
||||||
|
var block: AXBoolReturnBlock?
|
||||||
|
|
||||||
|
// if #available(iOS 17, *) {
|
||||||
|
// block = isAccessibilityElementBlock
|
||||||
|
// }
|
||||||
|
|
||||||
|
if block == nil {
|
||||||
|
block = bridge_isAccessibilityElementBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
if let block {
|
||||||
|
return block()
|
||||||
|
} else {
|
||||||
|
return _isAccessibilityElement
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
set {
|
||||||
|
_isAccessibilityElement = newValue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var _accessibilityLabel: String?
|
||||||
|
open override var accessibilityLabel: String? {
|
||||||
|
get {
|
||||||
|
var block: AXStringReturnBlock?
|
||||||
|
// if #available(iOS 17, *) {
|
||||||
|
// block = accessibilityLabelBlock
|
||||||
|
// }
|
||||||
|
|
||||||
|
if block == nil {
|
||||||
|
block = bridge_accessibilityLabelBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
if let block {
|
||||||
|
return block()
|
||||||
|
} else {
|
||||||
|
return _accessibilityLabel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
_accessibilityLabel = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var _accessibilityHint: String?
|
||||||
|
open override var accessibilityHint: String? {
|
||||||
|
get {
|
||||||
|
var block: AXStringReturnBlock?
|
||||||
|
// if #available(iOS 17, *) {
|
||||||
|
// block = accessibilityHintBlock
|
||||||
|
// }
|
||||||
|
|
||||||
|
if block == nil {
|
||||||
|
block = bridge_accessibilityHintBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
if let block {
|
||||||
|
return block()
|
||||||
|
} else {
|
||||||
|
return _accessibilityHint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
_accessibilityHint = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var _accessibilityValue: String?
|
||||||
|
open override var accessibilityValue: String? {
|
||||||
|
get {
|
||||||
|
var block: AXStringReturnBlock?
|
||||||
|
|
||||||
|
// if #available(iOS 17, *) {
|
||||||
|
// block = accessibilityHintBlock
|
||||||
|
// }
|
||||||
|
|
||||||
|
if block == nil {
|
||||||
|
block = bridge_accessibilityValueBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
if let block{
|
||||||
|
return block()
|
||||||
|
} else {
|
||||||
|
return _accessibilityValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
_accessibilityValue = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open override func accessibilityActivate() -> Bool {
|
||||||
|
guard isEnabled, isUserInteractionEnabled else { return false }
|
||||||
|
|
||||||
|
// if #available(iOS 17, *) {
|
||||||
|
// if let block = accessibilityAction {
|
||||||
|
// block(self)
|
||||||
|
// return true
|
||||||
|
// } else if let block = accessibilityActivateBlock {
|
||||||
|
// return block()
|
||||||
|
//
|
||||||
|
// } else if let block = bridge_accessibilityActivateBlock {
|
||||||
|
// return block()
|
||||||
|
//
|
||||||
|
// } else {
|
||||||
|
// return true
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// } else {
|
||||||
|
if let block = accessibilityAction {
|
||||||
|
block(self)
|
||||||
|
return true
|
||||||
|
|
||||||
|
} else if let block = bridge_accessibilityActivateBlock {
|
||||||
|
return block()
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return true
|
||||||
|
|
||||||
|
}
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -265,6 +265,12 @@ open class Notification: View {
|
|||||||
isAccessibilityElement = false
|
isAccessibilityElement = false
|
||||||
accessibilityElements = [closeButton, typeIcon, titleLabel, subTitleLabel, buttonGroup]
|
accessibilityElements = [closeButton, typeIcon, titleLabel, subTitleLabel, buttonGroup]
|
||||||
closeButton.accessibilityTraits = [.button]
|
closeButton.accessibilityTraits = [.button]
|
||||||
|
closeButton.accessibilityLabel = "Close Notification"
|
||||||
|
|
||||||
|
typeIcon.bridge_accessibilityLabelBlock = { [weak self] in
|
||||||
|
guard let self else { return "" }
|
||||||
|
return style.accessibleText
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resets to default settings.
|
/// Resets to default settings.
|
||||||
@ -372,12 +378,6 @@ open class Notification: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func updateAccessibility() {
|
|
||||||
super.updateAccessibility()
|
|
||||||
closeButton.accessibilityLabel = "Close Notification"
|
|
||||||
typeIcon.accessibilityLabel = style.accessibleText
|
|
||||||
}
|
|
||||||
|
|
||||||
private func setConstraints() {
|
private func setConstraints() {
|
||||||
labelViewAndButtonViewConstraint?.deactivate()
|
labelViewAndButtonViewConstraint?.deactivate()
|
||||||
labelViewBottomConstraint?.deactivate()
|
labelViewBottomConstraint?.deactivate()
|
||||||
|
|||||||
@ -86,6 +86,10 @@ open class Pagination: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var paginationDescription: String {
|
||||||
|
"Page \(selectedPage) of \(total) selected"
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Overrides
|
// MARK: - Overrides
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -148,14 +152,26 @@ open class Pagination: View {
|
|||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
self.selectedPage = max(0, self.selectedPage - 1)
|
self.selectedPage = max(0, self.selectedPage - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
collectionContainerView.bridge_accessibilityLabelBlock = { [weak self] in
|
||||||
|
guard let self else { return "" }
|
||||||
|
return "Pagination containing \(total) pages"
|
||||||
|
}
|
||||||
|
|
||||||
|
collectionContainerView.bridge_accessibilityValueBlock = { [weak self] in
|
||||||
|
guard let self else { return "" }
|
||||||
|
return paginationDescription
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///Updating the accessiblity values i.e elements, label, value other items for the component.
|
open override var accessibilityElements: [Any]? {
|
||||||
open override func updateAccessibility() {
|
get {
|
||||||
super.updateAccessibility()
|
let views: [UIView] = [previousButton, collectionContainerView, nextButton]
|
||||||
accessibilityElements = [previousButton, collectionContainerView, nextButton]
|
return views.filter({ $0.isHidden == false })
|
||||||
collectionContainerView.accessibilityLabel = "Pagination containing \(total) pages"
|
}
|
||||||
collectionContainerView.accessibilityValue = "Page \(selectedPage) of \(total) selected"
|
set {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to make changes to the View based off a change events or from local properties.
|
/// Used to make changes to the View based off a change events or from local properties.
|
||||||
@ -176,7 +192,7 @@ open class Pagination: View {
|
|||||||
updateSelection()
|
updateSelection()
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in
|
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in
|
||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
UIAccessibility.post(notification: .announcement, argument: "Page \(self.selectedPage) of \(self.total) selected")
|
UIAccessibility.post(notification: .announcement, argument: paginationDescription)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -78,11 +78,6 @@ open class PaginationButton: ButtonBase {
|
|||||||
tintColor = color
|
tintColor = color
|
||||||
super.updateView()
|
super.updateView()
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func accessibilityActivate() -> Bool {
|
|
||||||
sendActions(for: .touchUpInside)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension PaginationButton {
|
extension PaginationButton {
|
||||||
|
|||||||
@ -42,8 +42,6 @@ open class RadioBoxGroup: SelectorGroupBase<RadioBoxItem>, SelectorGroupSingleSe
|
|||||||
if let selectorModels {
|
if let selectorModels {
|
||||||
items = selectorModels.enumerated().map { index, model in
|
items = selectorModels.enumerated().map { index, model in
|
||||||
return RadioBoxItem().with {
|
return RadioBoxItem().with {
|
||||||
$0.accessibilityLabel = model.accessibileText
|
|
||||||
$0.accessibilityValue = "item \(index+1) of \(selectorModels.count)"
|
|
||||||
$0.text = model.text
|
$0.text = model.text
|
||||||
$0.textAttributes = model.textAttributes
|
$0.textAttributes = model.textAttributes
|
||||||
$0.subText = model.subText
|
$0.subText = model.subText
|
||||||
@ -56,7 +54,7 @@ open class RadioBoxGroup: SelectorGroupBase<RadioBoxItem>, SelectorGroupSingleSe
|
|||||||
$0.isSelected = model.selected
|
$0.isSelected = model.selected
|
||||||
$0.strikethrough = model.strikethrough
|
$0.strikethrough = model.strikethrough
|
||||||
$0.strikethroughAccessibilityText = model.strikethroughAccessibileText
|
$0.strikethroughAccessibilityText = model.strikethroughAccessibileText
|
||||||
$0.accessibilityValueText = "item \(index+1) of \(selectorModels.count)"
|
$0.selectorView.bridge_accessibilityValueBlock = { "item \(index+1) of \(selectorModels.count)" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -111,7 +109,7 @@ extension RadioBoxGroup {
|
|||||||
/// Current Surface and this is used to pass down to child objects that implement Surfacable
|
/// Current Surface and this is used to pass down to child objects that implement Surfacable
|
||||||
public var surface: Surface
|
public var surface: Surface
|
||||||
public var inputId: String?
|
public var inputId: String?
|
||||||
public var value: AnyHashable?
|
public var value: String?
|
||||||
public var accessibileText: String?
|
public var accessibileText: String?
|
||||||
public var text: String
|
public var text: String
|
||||||
/// Array of LabelAttributeModel objects used in rendering the text.
|
/// Array of LabelAttributeModel objects used in rendering the text.
|
||||||
@ -126,7 +124,7 @@ extension RadioBoxGroup {
|
|||||||
public var strikethrough: Bool = false
|
public var strikethrough: Bool = false
|
||||||
public var strikethroughAccessibileText: String
|
public var strikethroughAccessibileText: String
|
||||||
|
|
||||||
public init(disabled: Bool, surface: Surface = .light, inputId: String? = nil, value: AnyHashable? = nil,
|
public init(disabled: Bool, surface: Surface = .light, inputId: String? = nil, value: String? = nil,
|
||||||
text: String = "", textAttributes: [any LabelAttributeModel]? = nil,
|
text: String = "", textAttributes: [any LabelAttributeModel]? = nil,
|
||||||
subText: String? = nil, subTextAttributes: [any LabelAttributeModel]? = nil,
|
subText: String? = nil, subTextAttributes: [any LabelAttributeModel]? = nil,
|
||||||
subTextRight: String? = nil, subTextRightAttributes: [any LabelAttributeModel]? = nil,
|
subTextRight: String? = nil, subTextRightAttributes: [any LabelAttributeModel]? = nil,
|
||||||
|
|||||||
@ -74,9 +74,7 @@ open class RadioBoxItem: Control, Changeable, FormFieldable, Groupable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Selector for this RadioBox.
|
/// Selector for this RadioBox.
|
||||||
open var selectorView = UIView().with {
|
open var selectorView = View().with { $0.accessibilityIdentifier = "RadioBox" }
|
||||||
$0.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If provided, the RadioBox text will be rendered.
|
/// If provided, the RadioBox text will be rendered.
|
||||||
open var text: String? { didSet { setNeedsUpdate() } }
|
open var text: String? { didSet { setNeedsUpdate() } }
|
||||||
@ -127,11 +125,18 @@ open class RadioBoxItem: Control, Changeable, FormFieldable, Groupable {
|
|||||||
|
|
||||||
open var inputId: String? { didSet { setNeedsUpdate() } }
|
open var inputId: String? { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
open var value: AnyHashable? { hiddenValue }
|
open var value: String? { hiddenValue }
|
||||||
|
|
||||||
open var hiddenValue: AnyHashable? { didSet { setNeedsUpdate() } }
|
open var hiddenValue: String? { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
open var accessibilityValueText: String?
|
open override var accessibilityAction: ((Control) -> Void)? {
|
||||||
|
didSet {
|
||||||
|
selectorView.accessibilityAction = { [weak self] selectorItemBase in
|
||||||
|
guard let self else { return }
|
||||||
|
accessibilityAction?(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Configuration Properties
|
// MARK: - Configuration Properties
|
||||||
@ -165,16 +170,51 @@ open class RadioBoxItem: Control, Changeable, FormFieldable, Groupable {
|
|||||||
onClick = { control in
|
onClick = { control in
|
||||||
control.toggle()
|
control.toggle()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
selectorView.bridge_accessibilityLabelBlock = { [weak self] in
|
||||||
|
guard let self else { return "" }
|
||||||
|
var accessibilityLabels = [String]()
|
||||||
|
|
||||||
|
if isSelected {
|
||||||
|
accessibilityLabels.append("selected")
|
||||||
|
}
|
||||||
|
|
||||||
|
accessibilityLabels.append("Radiobox")
|
||||||
|
|
||||||
|
if let text, !text.isEmpty {
|
||||||
|
accessibilityLabels.append(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let text = subText, !text.isEmpty {
|
||||||
|
accessibilityLabels.append(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let text = subTextRight, !text.isEmpty {
|
||||||
|
accessibilityLabels.append(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
if strikethrough {
|
||||||
|
accessibilityLabels.append(strikethroughAccessibilityText)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isEnabled {
|
||||||
|
accessibilityLabels.append("dimmed")
|
||||||
|
}
|
||||||
|
|
||||||
|
return accessibilityLabels.joined(separator: ", ")
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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()
|
||||||
|
|
||||||
isAccessibilityElement = true
|
isAccessibilityElement = false
|
||||||
accessibilityTraits = .button
|
selectorView.isAccessibilityElement = true
|
||||||
|
selectorView.accessibilityTraits = .button
|
||||||
addSubview(selectorView)
|
addSubview(selectorView)
|
||||||
selectorView.isUserInteractionEnabled = false
|
selectorView.isUserInteractionEnabled = true
|
||||||
|
|
||||||
selectorView.addSubview(selectorStackView)
|
selectorView.addSubview(selectorStackView)
|
||||||
|
|
||||||
@ -226,6 +266,8 @@ open class RadioBoxItem: Control, Changeable, FormFieldable, Groupable {
|
|||||||
|
|
||||||
/// 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.
|
||||||
open func toggle() {
|
open func toggle() {
|
||||||
|
guard isEnabled else { return }
|
||||||
|
|
||||||
//removed error
|
//removed error
|
||||||
isSelected.toggle()
|
isSelected.toggle()
|
||||||
sendActions(for: .valueChanged)
|
sendActions(for: .valueChanged)
|
||||||
@ -239,26 +281,51 @@ open class RadioBoxItem: Control, Changeable, FormFieldable, Groupable {
|
|||||||
setNeedsLayout()
|
setNeedsLayout()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to update any Accessibility properties.
|
open override var accessibilityElements: [Any]? {
|
||||||
open override func updateAccessibility() {
|
get {
|
||||||
super.updateAccessibility()
|
var items = [Any]()
|
||||||
setAccessibilityLabel(for: [textLabel, subTextLabel, subTextRightLabel])
|
items.append(selectorView)
|
||||||
if let currentAccessibilityLabel = accessibilityLabel {
|
|
||||||
accessibilityLabel = "Radiobox, \(currentAccessibilityLabel)"
|
if let text = text, !text.isEmpty {
|
||||||
} else {
|
items.append(textLabel)
|
||||||
accessibilityLabel = "Radiobox"
|
}
|
||||||
|
|
||||||
|
if let text = subText, !text.isEmpty {
|
||||||
|
items.append(subTextLabel)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let text = subTextRight, !text.isEmpty {
|
||||||
|
items.append(subTextRightLabel)
|
||||||
|
}
|
||||||
|
return items
|
||||||
}
|
}
|
||||||
if let accessibilityValueText {
|
set {}
|
||||||
accessibilityValue = strikethrough
|
}
|
||||||
? "\(strikethroughAccessibilityText), \(accessibilityValueText)"
|
|
||||||
: accessibilityValueText
|
/// 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? {
|
||||||
|
guard isEnabled else { return super.hitTest(point, with: event) }
|
||||||
|
|
||||||
|
let textPoint = convert(point, to: textLabel)
|
||||||
|
let subTextPoint = convert(point, to: subTextLabel)
|
||||||
|
let subTextRightPoint = convert(point, to: subTextRightLabel)
|
||||||
|
|
||||||
|
if textLabel.isAction(for: textPoint) {
|
||||||
|
return textLabel
|
||||||
|
} else if subTextLabel.isAction(for: subTextPoint) {
|
||||||
|
return subTextLabel
|
||||||
|
} else if subTextRightLabel.isAction(for: subTextRightPoint) {
|
||||||
|
return subTextRightLabel
|
||||||
} else {
|
} else {
|
||||||
accessibilityValue = strikethrough
|
guard !UIAccessibility.isVoiceOverRunning else { return nil }
|
||||||
? "\(strikethroughAccessibilityText)"
|
return super.hitTest(point, with: event)
|
||||||
: accessibilityValueText
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open func getSelectorView() -> UIView {
|
||||||
|
selectorView
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Private Methods
|
// MARK: - Private Methods
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -62,7 +62,7 @@ open class RadioButton: SelectorBase {
|
|||||||
|
|
||||||
/// 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.
|
||||||
open override func toggle() {
|
open override func toggle() {
|
||||||
guard !isSelected else { return }
|
guard !isSelected, isEnabled else { return }
|
||||||
|
|
||||||
//removed error
|
//removed error
|
||||||
if showError && isSelected == false {
|
if showError && isSelected == false {
|
||||||
|
|||||||
@ -46,8 +46,6 @@ open class RadioButtonGroup: SelectorGroupBase<RadioButtonItem>, SelectorGroupSi
|
|||||||
$0.surface = model.surface
|
$0.surface = model.surface
|
||||||
$0.inputId = model.inputId
|
$0.inputId = model.inputId
|
||||||
$0.hiddenValue = model.value
|
$0.hiddenValue = model.value
|
||||||
$0.accessibilityLabel = model.accessibileText
|
|
||||||
$0.accessibilityValueText = "item \(index+1) of \(selectorModels.count)"
|
|
||||||
$0.labelText = model.labelText
|
$0.labelText = model.labelText
|
||||||
$0.labelTextAttributes = model.labelTextAttributes
|
$0.labelTextAttributes = model.labelTextAttributes
|
||||||
$0.childText = model.childText
|
$0.childText = model.childText
|
||||||
@ -55,6 +53,7 @@ open class RadioButtonGroup: SelectorGroupBase<RadioButtonItem>, SelectorGroupSi
|
|||||||
$0.isSelected = model.selected
|
$0.isSelected = model.selected
|
||||||
$0.errorText = model.errorText
|
$0.errorText = model.errorText
|
||||||
$0.showError = model.showError
|
$0.showError = model.showError
|
||||||
|
$0.selectorView.bridge_accessibilityValueBlock = { "item \(index+1) of \(selectorModels.count)" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,7 +34,7 @@ open class RadioButtonItem: SelectorItemBase<RadioButton> {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
/// 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.
|
||||||
open override func toggle() {
|
open override func toggle() {
|
||||||
guard !isSelected else { return }
|
guard !isSelected, isEnabled else { return }
|
||||||
|
|
||||||
//removed error
|
//removed error
|
||||||
if showError && isSelected == false {
|
if showError && isSelected == false {
|
||||||
|
|||||||
@ -89,8 +89,6 @@ extension Tabs {
|
|||||||
|
|
||||||
open override var shouldHighlight: Bool { false }
|
open override var shouldHighlight: Bool { false }
|
||||||
|
|
||||||
open var accessibilityValueText: String?
|
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Configuration
|
// MARK: - Configuration
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -151,6 +149,11 @@ extension Tabs {
|
|||||||
labelTopConstraint = label.pinTop(anchor: layoutGuide.topAnchor)
|
labelTopConstraint = label.pinTop(anchor: layoutGuide.topAnchor)
|
||||||
labelLeadingConstraint = label.pinLeading(anchor: layoutGuide.leadingAnchor)
|
labelLeadingConstraint = label.pinLeading(anchor: layoutGuide.leadingAnchor)
|
||||||
labelBottomConstraint = label.pinBottom(anchor: layoutGuide.bottomAnchor, priority: .defaultHigh)
|
labelBottomConstraint = label.pinBottom(anchor: layoutGuide.bottomAnchor, priority: .defaultHigh)
|
||||||
|
|
||||||
|
bridge_accessibilityLabelBlock = { [weak self] in
|
||||||
|
guard let self else { return "" }
|
||||||
|
return text
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to make changes to the View based off a change events or from local properties.
|
/// Used to make changes to the View based off a change events or from local properties.
|
||||||
@ -176,13 +179,6 @@ extension Tabs {
|
|||||||
setNeedsLayout()
|
setNeedsLayout()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to update any Accessibility properties.
|
|
||||||
open override func updateAccessibility() {
|
|
||||||
super.updateAccessibility()
|
|
||||||
accessibilityLabel = text
|
|
||||||
accessibilityValue = accessibilityValueText
|
|
||||||
}
|
|
||||||
|
|
||||||
open override func layoutSubviews() {
|
open override func layoutSubviews() {
|
||||||
super.layoutSubviews()
|
super.layoutSubviews()
|
||||||
|
|
||||||
|
|||||||
@ -305,7 +305,10 @@ open class Tabs: View {
|
|||||||
tabItem.orientation = orientation
|
tabItem.orientation = orientation
|
||||||
tabItem.surface = surface
|
tabItem.surface = surface
|
||||||
tabItem.indicatorPosition = indicatorPosition
|
tabItem.indicatorPosition = indicatorPosition
|
||||||
tabItem.accessibilityValueText = "\(index+1) of \(tabViews.count) Tabs"
|
tabItem.bridge_accessibilityValueBlock = { [weak self] in
|
||||||
|
guard let self else { return "" }
|
||||||
|
return "\(index+1) of \(tabViews.count) Tabs"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -94,12 +94,9 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable {
|
|||||||
|
|
||||||
/// This is the view that will be wrapped with the border for userInteraction.
|
/// This is the view that will be wrapped with the border for userInteraction.
|
||||||
/// The only subview of this view is the fieldStackView
|
/// The only subview of this view is the fieldStackView
|
||||||
internal var containerView: UIView = {
|
internal var containerView = View().with {
|
||||||
return UIView().with {
|
$0.isAccessibilityElement = true
|
||||||
$0.translatesAutoresizingMaskIntoConstraints = false
|
}
|
||||||
$0.isAccessibilityElement = true
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
/// This is set by a local method.
|
/// This is set by a local method.
|
||||||
internal var bottomContainerView: UIView!
|
internal var bottomContainerView: UIView!
|
||||||
@ -244,27 +241,6 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable {
|
|||||||
|
|
||||||
open var rules = [AnyRule<String>]()
|
open var rules = [AnyRule<String>]()
|
||||||
|
|
||||||
open var accessibilityLabelText: String {
|
|
||||||
var accessibilityLabels = [String]()
|
|
||||||
|
|
||||||
if let text = titleLabel.text?.trimmingCharacters(in: .whitespaces) {
|
|
||||||
accessibilityLabels.append(text)
|
|
||||||
}
|
|
||||||
if isReadOnly {
|
|
||||||
accessibilityLabels.append("read only")
|
|
||||||
}
|
|
||||||
if !isEnabled {
|
|
||||||
accessibilityLabels.append("dimmed")
|
|
||||||
}
|
|
||||||
if let errorText, showError {
|
|
||||||
accessibilityLabels.append("error, \(errorText)")
|
|
||||||
}
|
|
||||||
|
|
||||||
accessibilityLabels.append("\(Self.self)")
|
|
||||||
|
|
||||||
return accessibilityLabels.joined(separator: ", ")
|
|
||||||
}
|
|
||||||
|
|
||||||
open var accessibilityHintText: String = "Double tap to open"
|
open var accessibilityHintText: String = "Double tap to open"
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -333,6 +309,38 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable {
|
|||||||
titleLabel.textColorConfiguration = primaryColorConfiguration.eraseToAnyColorable()
|
titleLabel.textColorConfiguration = primaryColorConfiguration.eraseToAnyColorable()
|
||||||
errorLabel.textColorConfiguration = primaryColorConfiguration.eraseToAnyColorable()
|
errorLabel.textColorConfiguration = primaryColorConfiguration.eraseToAnyColorable()
|
||||||
helperLabel.textColorConfiguration = secondaryColorConfiguration.eraseToAnyColorable()
|
helperLabel.textColorConfiguration = secondaryColorConfiguration.eraseToAnyColorable()
|
||||||
|
|
||||||
|
containerView.bridge_accessibilityLabelBlock = { [weak self] in
|
||||||
|
guard let self else { return "" }
|
||||||
|
var accessibilityLabels = [String]()
|
||||||
|
|
||||||
|
if let text = titleLabel.text?.trimmingCharacters(in: .whitespaces) {
|
||||||
|
accessibilityLabels.append(text)
|
||||||
|
}
|
||||||
|
if isReadOnly {
|
||||||
|
accessibilityLabels.append("read only")
|
||||||
|
}
|
||||||
|
if !isEnabled {
|
||||||
|
accessibilityLabels.append("dimmed")
|
||||||
|
}
|
||||||
|
if let errorText, showError {
|
||||||
|
accessibilityLabels.append("error, \(errorText)")
|
||||||
|
}
|
||||||
|
|
||||||
|
accessibilityLabels.append("\(Self.self)")
|
||||||
|
|
||||||
|
return accessibilityLabels.joined(separator: ", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
containerView.bridge_accessibilityHintBlock = { [weak self] in
|
||||||
|
guard let self else { return "" }
|
||||||
|
return isReadOnly || !isEnabled ? "" : accessibilityHintText
|
||||||
|
}
|
||||||
|
|
||||||
|
containerView.bridge_accessibilityValueBlock = { [weak self] in
|
||||||
|
guard let self else { return "" }
|
||||||
|
return value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates the UI
|
/// Updates the UI
|
||||||
@ -472,13 +480,6 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func updateAccessibility() {
|
|
||||||
super.updateAccessibility()
|
|
||||||
containerView.accessibilityLabel = accessibilityLabelText
|
|
||||||
containerView.accessibilityHint = isReadOnly || !isEnabled ? "" : accessibilityHintText
|
|
||||||
containerView.accessibilityValue = value
|
|
||||||
}
|
|
||||||
|
|
||||||
open override var accessibilityElements: [Any]? {
|
open override var accessibilityElements: [Any]? {
|
||||||
get {
|
get {
|
||||||
var elements = [Any]()
|
var elements = [Any]()
|
||||||
|
|||||||
@ -47,6 +47,9 @@ open class TextField: UITextField, ViewProtocol, Errorable {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Properties
|
// MARK: - Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
/// Key of whether or not updateView() is called in setNeedsUpdate()
|
||||||
|
open var shouldUpdateView: Bool = true
|
||||||
|
|
||||||
private var formatLabel = Label().with {
|
private var formatLabel = Label().with {
|
||||||
$0.tag = 999
|
$0.tag = 999
|
||||||
$0.textColorConfiguration = ViewColorConfiguration().with {
|
$0.textColorConfiguration = ViewColorConfiguration().with {
|
||||||
@ -64,9 +67,6 @@ open class TextField: UITextField, ViewProtocol, Errorable {
|
|||||||
/// Will determine if a scaled font should be used for the titleLabel font.
|
/// Will determine if a scaled font should be used for the titleLabel font.
|
||||||
open var useScaledFont: Bool = false { didSet { setNeedsUpdate() } }
|
open var useScaledFont: Bool = false { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
/// Key of whether or not updateView() is called in setNeedsUpdate()
|
|
||||||
open var shouldUpdateView: 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() } }
|
||||||
@ -230,6 +230,138 @@ open class TextField: UITextField, ViewProtocol, Errorable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Accessibility
|
||||||
|
//--------------------------------------------------
|
||||||
|
open var accessibilityAction: ((TextField) -> Void)?
|
||||||
|
|
||||||
|
private var _isAccessibilityElement: Bool = false
|
||||||
|
open override var isAccessibilityElement: Bool {
|
||||||
|
get {
|
||||||
|
var block: AXBoolReturnBlock?
|
||||||
|
|
||||||
|
// if #available(iOS 17, *) {
|
||||||
|
// block = isAccessibilityElementBlock
|
||||||
|
// }
|
||||||
|
|
||||||
|
if block == nil {
|
||||||
|
block = bridge_isAccessibilityElementBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
if let block {
|
||||||
|
return block()
|
||||||
|
} else {
|
||||||
|
return _isAccessibilityElement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
_isAccessibilityElement = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var _accessibilityLabel: String?
|
||||||
|
open override var accessibilityLabel: String? {
|
||||||
|
get {
|
||||||
|
var block: AXStringReturnBlock?
|
||||||
|
// if #available(iOS 17, *) {
|
||||||
|
// block = accessibilityLabelBlock
|
||||||
|
// }
|
||||||
|
|
||||||
|
if block == nil {
|
||||||
|
block = bridge_accessibilityLabelBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
if let block {
|
||||||
|
return block()
|
||||||
|
} else {
|
||||||
|
return _accessibilityLabel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
_accessibilityLabel = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var _accessibilityHint: String?
|
||||||
|
open override var accessibilityHint: String? {
|
||||||
|
get {
|
||||||
|
var block: AXStringReturnBlock?
|
||||||
|
// if #available(iOS 17, *) {
|
||||||
|
// block = accessibilityHintBlock
|
||||||
|
// }
|
||||||
|
|
||||||
|
if block == nil {
|
||||||
|
block = bridge_accessibilityHintBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
if let block {
|
||||||
|
return block()
|
||||||
|
} else {
|
||||||
|
return _accessibilityHint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
_accessibilityHint = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var _accessibilityValue: String?
|
||||||
|
open override var accessibilityValue: String? {
|
||||||
|
get {
|
||||||
|
var block: AXStringReturnBlock?
|
||||||
|
|
||||||
|
// if #available(iOS 17, *) {
|
||||||
|
// block = accessibilityHintBlock
|
||||||
|
// }
|
||||||
|
|
||||||
|
if block == nil {
|
||||||
|
block = bridge_accessibilityValueBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
if let block{
|
||||||
|
return block()
|
||||||
|
} else {
|
||||||
|
return _accessibilityValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
_accessibilityValue = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open override func accessibilityActivate() -> Bool {
|
||||||
|
guard isEnabled, isUserInteractionEnabled else { return false }
|
||||||
|
|
||||||
|
// if #available(iOS 17, *) {
|
||||||
|
// if let block = accessibilityAction {
|
||||||
|
// block(self)
|
||||||
|
// return true
|
||||||
|
// } else if let block = accessibilityActivateBlock {
|
||||||
|
// return block()
|
||||||
|
//
|
||||||
|
// } else if let block = bridge_accessibilityActivateBlock {
|
||||||
|
// return block()
|
||||||
|
//
|
||||||
|
// } else {
|
||||||
|
// return super.accessibilityActivate()
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// } else {
|
||||||
|
if let block = accessibilityAction {
|
||||||
|
block(self)
|
||||||
|
return true
|
||||||
|
|
||||||
|
} else if let block = bridge_accessibilityActivateBlock {
|
||||||
|
return block()
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return super.accessibilityActivate()
|
||||||
|
|
||||||
|
}
|
||||||
|
// }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension UITextField {
|
extension UITextField {
|
||||||
|
|||||||
@ -145,6 +145,139 @@ open class TextView: UITextView, ViewProtocol, Errorable {
|
|||||||
setNeedsUpdate()
|
setNeedsUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Accessibility
|
||||||
|
//--------------------------------------------------
|
||||||
|
open var accessibilityAction: ((TextView) -> Void)?
|
||||||
|
|
||||||
|
private var _isAccessibilityElement: Bool = false
|
||||||
|
open override var isAccessibilityElement: Bool {
|
||||||
|
get {
|
||||||
|
var block: AXBoolReturnBlock?
|
||||||
|
|
||||||
|
// if #available(iOS 17, *) {
|
||||||
|
// block = isAccessibilityElementBlock
|
||||||
|
// }
|
||||||
|
|
||||||
|
if block == nil {
|
||||||
|
block = bridge_isAccessibilityElementBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
if let block {
|
||||||
|
return block()
|
||||||
|
} else {
|
||||||
|
return _isAccessibilityElement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
_isAccessibilityElement = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var _accessibilityLabel: String?
|
||||||
|
open override var accessibilityLabel: String? {
|
||||||
|
get {
|
||||||
|
var block: AXStringReturnBlock?
|
||||||
|
// if #available(iOS 17, *) {
|
||||||
|
// block = accessibilityLabelBlock
|
||||||
|
// }
|
||||||
|
|
||||||
|
if block == nil {
|
||||||
|
block = bridge_accessibilityLabelBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
if let block {
|
||||||
|
return block()
|
||||||
|
} else {
|
||||||
|
return _accessibilityLabel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
_accessibilityLabel = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var _accessibilityHint: String?
|
||||||
|
open override var accessibilityHint: String? {
|
||||||
|
get {
|
||||||
|
var block: AXStringReturnBlock?
|
||||||
|
// if #available(iOS 17, *) {
|
||||||
|
// block = accessibilityHintBlock
|
||||||
|
// }
|
||||||
|
|
||||||
|
if block == nil {
|
||||||
|
block = bridge_accessibilityHintBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
if let block {
|
||||||
|
return block()
|
||||||
|
} else {
|
||||||
|
return _accessibilityHint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
_accessibilityHint = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var _accessibilityValue: String?
|
||||||
|
open override var accessibilityValue: String? {
|
||||||
|
get {
|
||||||
|
var block: AXStringReturnBlock?
|
||||||
|
|
||||||
|
// if #available(iOS 17, *) {
|
||||||
|
// block = accessibilityHintBlock
|
||||||
|
// }
|
||||||
|
|
||||||
|
if block == nil {
|
||||||
|
block = bridge_accessibilityValueBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
if let block{
|
||||||
|
return block()
|
||||||
|
} else {
|
||||||
|
return _accessibilityValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
_accessibilityValue = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open override func accessibilityActivate() -> Bool {
|
||||||
|
guard isEnabled, isUserInteractionEnabled else { return false }
|
||||||
|
|
||||||
|
// if #available(iOS 17, *) {
|
||||||
|
// if let block = accessibilityAction {
|
||||||
|
// block(self)
|
||||||
|
// return true
|
||||||
|
// } else if let block = accessibilityActivateBlock {
|
||||||
|
// return block()
|
||||||
|
//
|
||||||
|
// } else if let block = bridge_accessibilityActivateBlock {
|
||||||
|
// return block()
|
||||||
|
//
|
||||||
|
// } else {
|
||||||
|
// return super.accessibilityActivate()
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// } else {
|
||||||
|
if let block = accessibilityAction {
|
||||||
|
block(self)
|
||||||
|
return true
|
||||||
|
|
||||||
|
} else if let block = bridge_accessibilityActivateBlock {
|
||||||
|
return block()
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return super.accessibilityActivate()
|
||||||
|
|
||||||
|
}
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Private Methods
|
// MARK: - Private Methods
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -266,6 +266,11 @@ open class TileContainerBase<PaddingType: DefaultValuing>: Control where Padding
|
|||||||
backgroundImageView.layer.cornerRadius = cornerRadius
|
backgroundImageView.layer.cornerRadius = cornerRadius
|
||||||
highlightView.layer.cornerRadius = cornerRadius
|
highlightView.layer.cornerRadius = cornerRadius
|
||||||
clipsToBounds = true
|
clipsToBounds = true
|
||||||
|
|
||||||
|
containerView.bridge_isAccessibilityElementBlock = { [weak self] in self?.onClickSubscriber != nil }
|
||||||
|
containerView.accessibilityHint = "Double tap to open."
|
||||||
|
containerView.accessibilityLabel = nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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
|
||||||
@ -338,13 +343,6 @@ open class TileContainerBase<PaddingType: DefaultValuing>: Control where Padding
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func updateAccessibility() {
|
|
||||||
super.updateAccessibility()
|
|
||||||
containerView.isAccessibilityElement = onClickSubscriber != nil
|
|
||||||
containerView.accessibilityHint = "Double tap to open."
|
|
||||||
containerView.accessibilityLabel = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
open override var accessibilityElements: [Any]? {
|
open override var accessibilityElements: [Any]? {
|
||||||
get {
|
get {
|
||||||
var items = [Any]()
|
var items = [Any]()
|
||||||
|
|||||||
@ -262,20 +262,21 @@ open class TitleLockup: View {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Overrides
|
// MARK: - Overrides
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
open override func updateAccessibility() {
|
open override var accessibilityElements: [Any]? {
|
||||||
super.updateAccessibility()
|
get {
|
||||||
var elements = [Any]()
|
var elements = [Any]()
|
||||||
if eyebrowModel != nil {
|
if eyebrowModel != nil {
|
||||||
elements.append(eyebrowLabel)
|
elements.append(eyebrowLabel)
|
||||||
|
}
|
||||||
|
if titleModel != nil {
|
||||||
|
elements.append(titleLabel)
|
||||||
|
}
|
||||||
|
if subTitleModel != nil {
|
||||||
|
elements.append(subTitleLabel)
|
||||||
|
}
|
||||||
|
return elements.count > 0 ? elements : nil
|
||||||
}
|
}
|
||||||
if titleModel != nil {
|
set {}
|
||||||
elements.append(titleLabel)
|
|
||||||
}
|
|
||||||
if subTitleModel != nil {
|
|
||||||
elements.append(subTitleLabel)
|
|
||||||
}
|
|
||||||
setAccessibilityLabel(for: elements.compactMap({$0 as? UIView}))
|
|
||||||
accessibilityElements = elements.count > 0 ? elements : nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resets to default settings.
|
/// Resets to default settings.
|
||||||
|
|||||||
@ -207,6 +207,14 @@ open class Toggle: Control, Changeable, FormFieldable {
|
|||||||
label.trailingAnchor.constraint(lessThanOrEqualTo: trailingAnchor)
|
label.trailingAnchor.constraint(lessThanOrEqualTo: trailingAnchor)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
bridge_accessibilityValueBlock = { [weak self] in
|
||||||
|
guard let self else { return "" }
|
||||||
|
if showText {
|
||||||
|
return isSelected ? onText : offText
|
||||||
|
} else {
|
||||||
|
return isSelected ? "On" : "Off"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resets to default settings.
|
/// Resets to default settings.
|
||||||
@ -239,16 +247,6 @@ open class Toggle: Control, Changeable, FormFieldable {
|
|||||||
toggleView.isOn = isOn
|
toggleView.isOn = isOn
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to update any Accessibility properties.
|
|
||||||
open override func updateAccessibility() {
|
|
||||||
super.updateAccessibility()
|
|
||||||
if showText {
|
|
||||||
accessibilityValue = isSelected ? onText : offText
|
|
||||||
} else {
|
|
||||||
accessibilityValue = isSelected ? "On" : "Off"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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.
|
||||||
open func toggle() {
|
open func toggle() {
|
||||||
isOn.toggle()
|
isOn.toggle()
|
||||||
|
|||||||
@ -153,7 +153,7 @@ open class ToggleView: Control, Changeable, FormFieldable {
|
|||||||
// Update shadow layers frames to match the view's bounds
|
// Update shadow layers frames to match the view's bounds
|
||||||
knobView.layer.insertSublayer(shadowLayer1, at: 0)
|
knobView.layer.insertSublayer(shadowLayer1, at: 0)
|
||||||
knobView.layer.insertSublayer(shadowLayer2, at: 0)
|
knobView.layer.insertSublayer(shadowLayer2, at: 0)
|
||||||
|
accessibilityLabel = "Toggle"
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resets to default settings.
|
/// Resets to default settings.
|
||||||
@ -177,13 +177,6 @@ open class ToggleView: Control, Changeable, FormFieldable {
|
|||||||
updateToggle()
|
updateToggle()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to update any Accessibility properties.
|
|
||||||
open override func updateAccessibility() {
|
|
||||||
super.updateAccessibility()
|
|
||||||
|
|
||||||
accessibilityLabel = "Toggle"
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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.
|
||||||
open func toggle() {
|
open func toggle() {
|
||||||
isOn.toggle()
|
isOn.toggle()
|
||||||
|
|||||||
@ -138,6 +138,24 @@ open class Tooltip: Control, TooltipLaunchable {
|
|||||||
contentView: tooltip.contentView),
|
contentView: tooltip.contentView),
|
||||||
presenter: self)
|
presenter: self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bridge_accessibilityLabelBlock = { [weak self] in
|
||||||
|
guard let self else { return "" }
|
||||||
|
var label = title
|
||||||
|
if label == nil {
|
||||||
|
label = content
|
||||||
|
}
|
||||||
|
if let label, !label.isEmpty {
|
||||||
|
return label
|
||||||
|
} else {
|
||||||
|
return "Modal"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bridge_accessibilityHintBlock = { [weak self] in
|
||||||
|
guard let self else { return "" }
|
||||||
|
return isEnabled ? "Double tap to open." : ""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resets to default settings.
|
/// Resets to default settings.
|
||||||
@ -164,22 +182,6 @@ open class Tooltip: Control, TooltipLaunchable {
|
|||||||
icon.color = iconColorConfiguration.getColor(self)
|
icon.color = iconColorConfiguration.getColor(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to update any Accessibility properties.
|
|
||||||
open override func updateAccessibility() {
|
|
||||||
super.updateAccessibility()
|
|
||||||
|
|
||||||
var label = title
|
|
||||||
if label == nil {
|
|
||||||
label = content
|
|
||||||
}
|
|
||||||
if let label, !label.isEmpty {
|
|
||||||
accessibilityLabel = label
|
|
||||||
} else {
|
|
||||||
accessibilityLabel = "Modal"
|
|
||||||
}
|
|
||||||
accessibilityHint = isEnabled ? "Double tap to open." : ""
|
|
||||||
}
|
|
||||||
|
|
||||||
public static func accessibleText(for title: String?, content: String?, closeButtonText: String) -> String {
|
public static func accessibleText(for title: String?, content: String?, closeButtonText: String) -> String {
|
||||||
var label = ""
|
var label = ""
|
||||||
if let title {
|
if let title {
|
||||||
|
|||||||
@ -219,15 +219,19 @@ open class TooltipDialog: View, UIScrollViewDelegate {
|
|||||||
/// Used to update any Accessibility properties.
|
/// Used to update any Accessibility properties.
|
||||||
open override func updateAccessibility() {
|
open override func updateAccessibility() {
|
||||||
super.updateAccessibility()
|
super.updateAccessibility()
|
||||||
|
|
||||||
primaryAccessibilityElement.accessibilityHint = "Double tap on the \(tooltipModel.closeButtonText) button to close."
|
primaryAccessibilityElement.accessibilityHint = "Double tap on the \(tooltipModel.closeButtonText) button to close."
|
||||||
primaryAccessibilityElement.accessibilityFrameInContainerSpace = .init(origin: .zero, size: frame.size)
|
primaryAccessibilityElement.accessibilityFrameInContainerSpace = .init(origin: .zero, size: frame.size)
|
||||||
|
}
|
||||||
|
|
||||||
var elements: [Any] = [primaryAccessibilityElement]
|
open override var accessibilityElements: [Any]? {
|
||||||
contentStackView.arrangedSubviews.forEach{ elements.append($0) }
|
get {
|
||||||
elements.append(closeButton)
|
var elements: [Any] = [primaryAccessibilityElement]
|
||||||
|
contentStackView.arrangedSubviews.forEach{ elements.append($0) }
|
||||||
|
elements.append(closeButton)
|
||||||
|
|
||||||
accessibilityElements = elements
|
return elements
|
||||||
|
}
|
||||||
|
set {}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -62,7 +62,7 @@ extension UIView {
|
|||||||
} else {
|
} else {
|
||||||
removeDebugBorder()
|
removeDebugBorder()
|
||||||
}
|
}
|
||||||
if let view = self as? ViewProtocol {
|
if let view = self as? (any ViewProtocol) {
|
||||||
view.updateView()
|
view.updateView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
95
VDS/Protocols/AccessibilityUpdatable.swift
Normal file
95
VDS/Protocols/AccessibilityUpdatable.swift
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
//
|
||||||
|
// AccessibilityUpdatable.swift
|
||||||
|
// VDS
|
||||||
|
//
|
||||||
|
// Created by Matt Bruce on 6/20/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
public protocol AccessibilityUpdatable {
|
||||||
|
var bridge_isAccessibilityElementBlock: AXBoolReturnBlock? { get set }
|
||||||
|
|
||||||
|
var bridge_accessibilityLabelBlock: AXStringReturnBlock? { get set }
|
||||||
|
|
||||||
|
var bridge_accessibilityValueBlock: AXStringReturnBlock? { get set }
|
||||||
|
|
||||||
|
var bridge_accessibilityHintBlock: AXStringReturnBlock? { get set }
|
||||||
|
|
||||||
|
var bridge_accessibilityActivateBlock: AXBoolReturnBlock? { get set }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct AccessibilityBridge {
|
||||||
|
static var isAccessibilityElementBlockKey: UInt8 = 0
|
||||||
|
static var activateBlockKey: UInt8 = 1
|
||||||
|
static var valueBlockKey: UInt8 = 2
|
||||||
|
static var hintBlockKey: UInt8 = 3
|
||||||
|
static var labelBlockKey: UInt8 = 4
|
||||||
|
}
|
||||||
|
|
||||||
|
extension AccessibilityUpdatable where Self: NSObject {
|
||||||
|
|
||||||
|
public var bridge_isAccessibilityElementBlock: AXBoolReturnBlock? {
|
||||||
|
get {
|
||||||
|
return objc_getAssociatedObject(self, &AccessibilityBridge.isAccessibilityElementBlockKey) as? AXBoolReturnBlock
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
objc_setAssociatedObject(self, &AccessibilityBridge.isAccessibilityElementBlockKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||||
|
// if #available(iOS 17, *) {
|
||||||
|
// self.isAccessibilityElementBlock = newValue
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public var bridge_accessibilityActivateBlock: AXBoolReturnBlock? {
|
||||||
|
get {
|
||||||
|
return objc_getAssociatedObject(self, &AccessibilityBridge.activateBlockKey) as? AXBoolReturnBlock
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
objc_setAssociatedObject(self, &AccessibilityBridge.activateBlockKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||||
|
// if #available(iOS 17, *) {
|
||||||
|
// self.accessibilityActivateBlock = newValue
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public var bridge_accessibilityValueBlock: AXStringReturnBlock? {
|
||||||
|
get {
|
||||||
|
return objc_getAssociatedObject(self, &AccessibilityBridge.valueBlockKey) as? AXStringReturnBlock
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
objc_setAssociatedObject(self, &AccessibilityBridge.valueBlockKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||||
|
// if #available(iOS 17, *) {
|
||||||
|
// self.accessibilityValueBlock = newValue
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public var bridge_accessibilityHintBlock: AXStringReturnBlock? {
|
||||||
|
get {
|
||||||
|
return objc_getAssociatedObject(self, &AccessibilityBridge.hintBlockKey) as? AXStringReturnBlock
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
objc_setAssociatedObject(self, &AccessibilityBridge.hintBlockKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||||
|
// if #available(iOS 17, *) {
|
||||||
|
// self.accessibilityHintBlock = newValue
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public var bridge_accessibilityLabelBlock: AXStringReturnBlock? {
|
||||||
|
get {
|
||||||
|
return objc_getAssociatedObject(self, &AccessibilityBridge.labelBlockKey) as? AXStringReturnBlock
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
objc_setAssociatedObject(self, &AccessibilityBridge.labelBlockKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||||
|
// if #available(iOS 17, *) {
|
||||||
|
// self.accessibilityLabelBlock = newValue
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@ -9,6 +9,4 @@ import Foundation
|
|||||||
|
|
||||||
public protocol Groupable: Control {
|
public protocol Groupable: Control {
|
||||||
|
|
||||||
/// Property used to add context to the Grouping of a set.
|
|
||||||
var accessibilityValueText: String? { get set }
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,13 +9,16 @@ import Foundation
|
|||||||
import UIKit
|
import UIKit
|
||||||
import Combine
|
import Combine
|
||||||
|
|
||||||
public protocol ViewProtocol: AnyObject, Initable, Resettable, Enabling, Surfaceable {
|
public protocol ViewProtocol: AnyObject, Initable, Resettable, Enabling, Surfaceable, AccessibilityUpdatable {
|
||||||
/// Set of Subscribers for any Publishers for this Control.
|
/// Set of Subscribers for any Publishers for this Control.
|
||||||
var subscribers: Set<AnyCancellable> { get set }
|
var subscribers: Set<AnyCancellable> { get set }
|
||||||
|
|
||||||
/// 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 }
|
||||||
|
|
||||||
|
/// Used for setting an implementation for the default Accessible Action
|
||||||
|
var accessibilityAction: ((Self) -> Void)? { get set }
|
||||||
|
|
||||||
/// Executed on initialization for this View.
|
/// Executed on initialization for this View.
|
||||||
func initialSetup()
|
func initialSetup()
|
||||||
|
|
||||||
@ -30,6 +33,7 @@ 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 {
|
||||||
|
|||||||
@ -1,9 +1,12 @@
|
|||||||
|
1.0.68
|
||||||
|
----------------
|
||||||
|
- CXTDT-553663 - DropdownSelect - Accessibility - has popup
|
||||||
|
|
||||||
1.0.67
|
1.0.67
|
||||||
----------------
|
----------------
|
||||||
- CXTDT-568463 - Calendar - On long press, hover randomizes
|
- CXTDT-568463 - Calendar - On long press, hover randomizes
|
||||||
- CXTDT-568412 - Calendar - Incorrect side nav icon size
|
- CXTDT-568412 - Calendar - Incorrect side nav icon size
|
||||||
- CXTDT-568422 - Calendar - DarkMode Legend icon fill using Light mode color
|
- CXTDT-568422 - Calendar - DarkMode Legend icon fill using Light mode color
|
||||||
- CXTDT-553663 - DropdownSelect - Accessibility - has popup
|
|
||||||
- CXTDT-565796 - DropdownSelect - Accessibility
|
- CXTDT-565796 - DropdownSelect - Accessibility
|
||||||
- CXTDT-560458 - Dropdown/TextArea - Different voiceover
|
- CXTDT-560458 - Dropdown/TextArea - Different voiceover
|
||||||
- CXTDT-565106 - InputField - CreditCard - Icons
|
- CXTDT-565106 - InputField - CreditCard - Icons
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user