more code refactoring

Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
This commit is contained in:
Matt Bruce 2024-06-20 12:37:19 -05:00
parent d8c3ba2e33
commit 73f27d1e8b
34 changed files with 399 additions and 409 deletions

View File

@ -127,8 +127,6 @@ open class Control: UIControl, ViewProtocol, UserInfoable, Clickable {
//--------------------------------------------------
// MARK: - Accessibility
//--------------------------------------------------
open var shouldUpdateAccessibility: Bool = true
open var accessibilityAction: ((Control) -> Void)?
private var _isAccessibilityElement: Bool = false

View File

@ -104,6 +104,16 @@ open class SelectorBase: Control, SelectorControlable {
onClick = { control in
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.
@ -120,13 +130,6 @@ open class SelectorBase: Control, SelectorControlable {
layoutIfNeeded()
}
/// Used to update any Accessibility properties.ß
open override func updateAccessibility() {
super.updateAccessibility()
accessibilityLabel = "\(Self.self)\(showError ? ", error" : "")"
accessibilityHint = !isEnabled ? "" : "Double tap to open."
}
/// This will change the state of the Selector and execute the actionBlock if provided.
open func toggle() { }

View File

@ -145,8 +145,6 @@ open class SelectorItemBase<Selector: SelectorBase>: Control, Errorable, Changea
open var hiddenValue: AnyHashable? { didSet { setNeedsUpdate() } }
open var accessibilityValueText: String?
open override var accessibilityAction: ((Control) -> Void)? {
didSet {
selectorView.accessibilityAction = { [weak self] selectorItemBase in
@ -156,34 +154,6 @@ open class SelectorItemBase<Selector: SelectorBase>: Control, Errorable, Changea
}
}
open var accessibilityLabelText: String {
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: ", ")
}
//--------------------------------------------------
// MARK: - Overrides
//--------------------------------------------------
@ -199,6 +169,41 @@ open class SelectorItemBase<Selector: SelectorBase>: Control, Errorable, Changea
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.
@ -206,8 +211,6 @@ open class SelectorItemBase<Selector: SelectorBase>: Control, Errorable, Changea
super.setup()
selectorView.isAccessibilityElement = true
selectorView.shouldUpdateAccessibility = false
isAccessibilityElement = false
addSubview(mainStackView)
@ -236,14 +239,6 @@ open class SelectorItemBase<Selector: SelectorBase>: Control, Errorable, Changea
selectorView.surface = surface
}
/// Used to update any Accessibility properties.
open override func updateAccessibility() {
super.updateAccessibility()
selectorView.accessibilityLabel = accessibilityLabelText
selectorView.accessibilityHint = !isEnabled ? "" : "Double tap to activate."
accessibilityValue = accessibilityValueText
}
open override var accessibilityElements: [Any]? {
get {
var elements = [Any]()

View File

@ -94,8 +94,6 @@ open class View: UIView, ViewProtocol, UserInfoable {
//--------------------------------------------------
// MARK: - Accessibility
//--------------------------------------------------
open var shouldUpdateAccessibility: Bool = true
open var accessibilityAction: ((View) -> Void)?
private var _isAccessibilityElement: Bool = false
@ -219,7 +217,7 @@ open class View: UIView, ViewProtocol, UserInfoable {
return block()
} else {
return true
return super.accessibilityActivate()
}
// }

View File

@ -13,7 +13,8 @@ public class AccessibilityActionElement: UIAccessibilityElement {
public var accessibilityAction: AXVoidReturnBlock?
public override func accessibilityActivate() -> Bool {
accessibilityAction?()
guard let accessibilityAction else { return super.accessibilityActivate() }
accessibilityAction()
return true
}
}

View File

@ -147,6 +147,11 @@ open class Badge: View {
label.widthGreaterThanEqualTo(constant: minWidth)
maxWidthConstraint = label.widthLessThanEqualTo(constant: 0).with { $0.isActive = false }
clipsToBounds = true
bridge_accessibilityLabelBlock = { [weak self] in
guard let self else { return "" }
return text
}
}
/// Resets to default settings.
@ -179,10 +184,4 @@ open class Badge: View {
label.surface = surface
label.isEnabled = isEnabled
}
open override func updateAccessibility() {
super.updateAccessibility()
accessibilityLabel = text
}
}

View File

@ -292,6 +292,16 @@ open class BadgeIndicator: View {
label.centerYAnchor.constraint(equalTo: badgeView.centerYAnchor).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.
@ -347,17 +357,6 @@ open class BadgeIndicator: View {
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() {
super.layoutSubviews()

View File

@ -82,6 +82,15 @@ open class BreadcrumbItem: ButtonBase {
isAccessibilityElement = true
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.
@ -134,10 +143,4 @@ open class BreadcrumbItem: ButtonBase {
setNeedsUpdate()
}
/// Used to update any Accessibility properties.
open override func updateAccessibility() {
super.updateAccessibility()
accessibilityLabel = text
}
}

View File

@ -175,8 +175,6 @@ open class ButtonBase: UIButton, ViewProtocol, UserInfoable, Clickable {
//--------------------------------------------------
// MARK: - Accessibility
//--------------------------------------------------
open var shouldUpdateAccessibility: Bool = true
open var accessibilityAction: ((ButtonBase) -> Void)?
private var _isAccessibilityElement: Bool = false

View File

@ -105,6 +105,12 @@ open class TextLink: ButtonBase {
lineHeightConstraint = line.height(constant: 1)
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.

View File

@ -86,6 +86,12 @@ open class TextLinkCaret: ButtonBase {
accessibilityTraits = .link
titleLabel?.numberOfLines = 0
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.

View File

@ -47,8 +47,6 @@ open class CheckboxGroup: SelectorGroupBase<CheckboxItem>, SelectorGroupMultiSel
$0.surface = model.surface
$0.inputId = model.inputId
$0.hiddenValue = model.value
$0.accessibilityLabel = model.accessibileText
$0.accessibilityValueText = "item \(index+1) of \(selectorModels.count)"
$0.labelText = model.labelText
$0.labelTextAttributes = model.labelTextAttributes
$0.childText = model.childText
@ -56,6 +54,7 @@ open class CheckboxGroup: SelectorGroupBase<CheckboxItem>, SelectorGroupMultiSel
$0.isSelected = model.selected
$0.errorText = model.errorText
$0.showError = model.showError
$0.selectorView.bridge_accessibilityValueBlock = { "item \(index+1) of \(selectorModels.count)" }
}
}
}

View File

@ -464,16 +464,18 @@ open class ButtonIcon: Control, Changeable {
setNeedsLayout()
}
open override func updateAccessibility() {
super.updateAccessibility()
var elements = [Any]()
if iconName != nil {
elements.append(icon)
open override var accessibilityElements: [Any]? {
get {
var elements = [Any]()
if iconName != nil {
elements.append(icon)
}
if badgeIndicatorModel != nil && showBadgeIndicator {
elements.append(badgeIndicator)
}
return elements.count > 0 ? elements : nil
}
if badgeIndicatorModel != nil && showBadgeIndicator {
elements.append(badgeIndicator)
}
accessibilityElements = elements.count > 0 ? elements : nil
set { }
}
open override func layoutSubviews() {

View File

@ -94,6 +94,12 @@ open class Icon: View {
isAccessibilityElement = true
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.
@ -119,11 +125,6 @@ open class Icon: View {
color = VDSColor.paletteBlack
imageView.image = nil
}
open override func updateAccessibility() {
super.updateAccessibility()
accessibilityLabel = name?.rawValue ?? "icon"
}
}
extension UIImage {

View File

@ -213,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() {
shouldUpdateView = false
@ -240,7 +245,6 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
}
open func updateAccessibility() {
accessibilityLabel = text
if isEnabled {
accessibilityTraits.remove(.notEnabled)
} else {
@ -456,8 +460,6 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
//--------------------------------------------------
// MARK: - Accessibility
//--------------------------------------------------
open var shouldUpdateAccessibility: Bool = true
open var accessibilityAction: ((Label) -> Void)?
private var _isAccessibilityElement: Bool = false

View File

@ -265,6 +265,12 @@ open class Notification: View {
isAccessibilityElement = false
accessibilityElements = [closeButton, typeIcon, titleLabel, subTitleLabel, buttonGroup]
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.
@ -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() {
labelViewAndButtonViewConstraint?.deactivate()
labelViewBottomConstraint?.deactivate()

View File

@ -86,6 +86,10 @@ open class Pagination: View {
}
}
private var paginationDescription: String {
"Page \(selectedPage) of \(total) selected"
}
//--------------------------------------------------
// MARK: - Overrides
//--------------------------------------------------
@ -148,14 +152,26 @@ open class Pagination: View {
guard let self else { return }
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 func updateAccessibility() {
super.updateAccessibility()
accessibilityElements = [previousButton, collectionContainerView, nextButton]
collectionContainerView.accessibilityLabel = "Pagination containing \(total) pages"
collectionContainerView.accessibilityValue = "Page \(selectedPage) of \(total) selected"
open override var accessibilityElements: [Any]? {
get {
let views: [UIView] = [previousButton, collectionContainerView, nextButton]
return views.filter({ $0.isHidden == false })
}
set {
}
}
/// 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()
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in
guard let self else { return }
UIAccessibility.post(notification: .announcement, argument: "Page \(self.selectedPage) of \(self.total) selected")
UIAccessibility.post(notification: .announcement, argument: paginationDescription)
}
}

View File

@ -42,8 +42,6 @@ open class RadioBoxGroup: SelectorGroupBase<RadioBoxItem>, SelectorGroupSingleSe
if let selectorModels {
items = selectorModels.enumerated().map { index, model in
return RadioBoxItem().with {
$0.accessibilityLabel = model.accessibileText
$0.accessibilityValue = "item \(index+1) of \(selectorModels.count)"
$0.text = model.text
$0.textAttributes = model.textAttributes
$0.subText = model.subText
@ -56,7 +54,7 @@ open class RadioBoxGroup: SelectorGroupBase<RadioBoxItem>, SelectorGroupSingleSe
$0.isSelected = model.selected
$0.strikethrough = model.strikethrough
$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
public var surface: Surface
public var inputId: String?
public var value: AnyHashable?
public var value: String?
public var accessibileText: String?
public var text: String
/// Array of LabelAttributeModel objects used in rendering the text.
@ -126,7 +124,7 @@ extension RadioBoxGroup {
public var strikethrough: Bool = false
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,
subText: String? = nil, subTextAttributes: [any LabelAttributeModel]? = nil,
subTextRight: String? = nil, subTextRightAttributes: [any LabelAttributeModel]? = nil,

View File

@ -74,7 +74,7 @@ open class RadioBoxItem: Control, Changeable, FormFieldable, Groupable {
}
/// Selector for this RadioBox.
open var selectorView = View()
open var selectorView = View().with { $0.accessibilityIdentifier = "RadioBox" }
/// If provided, the RadioBox text will be rendered.
open var text: String? { didSet { setNeedsUpdate() } }
@ -125,11 +125,9 @@ open class RadioBoxItem: Control, Changeable, FormFieldable, Groupable {
open var inputId: String? { didSet { setNeedsUpdate() } }
open var value: AnyHashable? { hiddenValue }
open var value: String? { hiddenValue }
open var hiddenValue: AnyHashable? { didSet { setNeedsUpdate() } }
open var accessibilityValueText: String?
open var hiddenValue: String? { didSet { setNeedsUpdate() } }
open override var accessibilityAction: ((Control) -> Void)? {
didSet {
@ -140,34 +138,6 @@ open class RadioBoxItem: Control, Changeable, FormFieldable, Groupable {
}
}
open var accessibilityLabelText: String {
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 !isEnabled {
accessibilityLabels.append("dimmed")
}
return accessibilityLabels.joined(separator: ", ")
}
//--------------------------------------------------
// MARK: - Configuration Properties
//--------------------------------------------------
@ -201,13 +171,37 @@ open class RadioBoxItem: Control, Changeable, FormFieldable, Groupable {
control.toggle()
}
if #available(iOS 17.0, *) {
accessibilityHintBlock = { [weak self] in
selectorView.bridge_accessibilityLabelBlock = { [weak self] in
guard let self else { return "" }
var accessibilityLabels = [String]()
return "foo"
if isSelected {
accessibilityLabels.append("selected")
}
} else {
// Fallback on earlier versions
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: ", ")
}
}
@ -287,22 +281,6 @@ open class RadioBoxItem: Control, Changeable, FormFieldable, Groupable {
setNeedsLayout()
}
/// Used to update any Accessibility properties.
open override func updateAccessibility() {
super.updateAccessibility()
selectorView.accessibilityLabel = accessibilityLabelText
if let accessibilityValueText {
selectorView.accessibilityValue = strikethrough
? "\(strikethroughAccessibilityText), \(accessibilityValueText)"
: accessibilityValueText
} else {
selectorView.accessibilityValue = strikethrough
? "\(strikethroughAccessibilityText)"
: accessibilityValueText
}
}
open override var accessibilityElements: [Any]? {
get {
var items = [Any]()

View File

@ -46,8 +46,6 @@ open class RadioButtonGroup: SelectorGroupBase<RadioButtonItem>, SelectorGroupSi
$0.surface = model.surface
$0.inputId = model.inputId
$0.hiddenValue = model.value
$0.accessibilityLabel = model.accessibileText
$0.accessibilityValueText = "item \(index+1) of \(selectorModels.count)"
$0.labelText = model.labelText
$0.labelTextAttributes = model.labelTextAttributes
$0.childText = model.childText
@ -55,6 +53,7 @@ open class RadioButtonGroup: SelectorGroupBase<RadioButtonItem>, SelectorGroupSi
$0.isSelected = model.selected
$0.errorText = model.errorText
$0.showError = model.showError
$0.selectorView.bridge_accessibilityValueBlock = { "item \(index+1) of \(selectorModels.count)" }
}
}
}

View File

@ -89,8 +89,6 @@ extension Tabs {
open override var shouldHighlight: Bool { false }
open var accessibilityValueText: String?
//--------------------------------------------------
// MARK: - Configuration
//--------------------------------------------------
@ -151,6 +149,11 @@ extension Tabs {
labelTopConstraint = label.pinTop(anchor: layoutGuide.topAnchor)
labelLeadingConstraint = label.pinLeading(anchor: layoutGuide.leadingAnchor)
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.
@ -176,13 +179,6 @@ extension Tabs {
setNeedsLayout()
}
/// Used to update any Accessibility properties.
open override func updateAccessibility() {
super.updateAccessibility()
accessibilityLabel = text
accessibilityValue = accessibilityValueText
}
open override func layoutSubviews() {
super.layoutSubviews()

View File

@ -305,7 +305,10 @@ open class Tabs: View {
tabItem.orientation = orientation
tabItem.surface = surface
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"
}
}
}

View File

@ -94,12 +94,9 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable {
/// This is the view that will be wrapped with the border for userInteraction.
/// The only subview of this view is the fieldStackView
internal var containerView: UIView = {
return UIView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.isAccessibilityElement = true
}
}()
internal var containerView = View().with {
$0.isAccessibilityElement = true
}
/// This is set by a local method.
internal var bottomContainerView: UIView!
@ -244,27 +241,6 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable {
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"
//--------------------------------------------------
@ -333,6 +309,38 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable {
titleLabel.textColorConfiguration = primaryColorConfiguration.eraseToAnyColorable()
errorLabel.textColorConfiguration = primaryColorConfiguration.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
@ -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]? {
get {
var elements = [Any]()

View File

@ -67,8 +67,6 @@ open class TextField: UITextField, ViewProtocol, Errorable {
/// Will determine if a scaled font should be used for the titleLabel font.
open var useScaledFont: Bool = false { didSet { setNeedsUpdate() } }
open var shouldUpdateAccessibility: Bool = true
open var surface: Surface = .light { didSet { setNeedsUpdate() } }
open var showError: Bool = false { didSet { setNeedsUpdate() } }

View File

@ -149,8 +149,6 @@ open class TextView: UITextView, ViewProtocol, Errorable {
//--------------------------------------------------
// MARK: - Accessibility
//--------------------------------------------------
open var shouldUpdateAccessibility: Bool = true
open var accessibilityAction: ((TextView) -> Void)?
private var _isAccessibilityElement: Bool = false

View File

@ -266,6 +266,11 @@ open class TileContainerBase<PaddingType: DefaultValuing>: Control where Padding
backgroundImageView.layer.cornerRadius = cornerRadius
highlightView.layer.cornerRadius = cornerRadius
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
@ -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]? {
get {
var items = [Any]()

View File

@ -262,20 +262,21 @@ open class TitleLockup: View {
//--------------------------------------------------
// MARK: - Overrides
//--------------------------------------------------
open override func updateAccessibility() {
super.updateAccessibility()
var elements = [Any]()
if eyebrowModel != nil {
elements.append(eyebrowLabel)
open override var accessibilityElements: [Any]? {
get {
var elements = [Any]()
if eyebrowModel != nil {
elements.append(eyebrowLabel)
}
if titleModel != nil {
elements.append(titleLabel)
}
if subTitleModel != nil {
elements.append(subTitleLabel)
}
return elements.count > 0 ? elements : nil
}
if titleModel != nil {
elements.append(titleLabel)
}
if subTitleModel != nil {
elements.append(subTitleLabel)
}
setAccessibilityLabel(for: elements.compactMap({$0 as? UIView}))
accessibilityElements = elements.count > 0 ? elements : nil
set {}
}
/// Resets to default settings.

View File

@ -207,6 +207,14 @@ open class Toggle: Control, Changeable, FormFieldable {
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.
@ -239,16 +247,6 @@ open class Toggle: Control, Changeable, FormFieldable {
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.
open func toggle() {
isOn.toggle()

View File

@ -153,7 +153,7 @@ open class ToggleView: Control, Changeable, FormFieldable {
// Update shadow layers frames to match the view's bounds
knobView.layer.insertSublayer(shadowLayer1, at: 0)
knobView.layer.insertSublayer(shadowLayer2, at: 0)
accessibilityLabel = "Toggle"
}
/// Resets to default settings.
@ -177,13 +177,6 @@ open class ToggleView: Control, Changeable, FormFieldable {
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.
open func toggle() {
isOn.toggle()

View File

@ -138,6 +138,24 @@ open class Tooltip: Control, TooltipLaunchable {
contentView: tooltip.contentView),
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.
@ -164,22 +182,6 @@ open class Tooltip: Control, TooltipLaunchable {
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 {
var label = ""
if let title {

View File

@ -219,15 +219,19 @@ open class TooltipDialog: View, UIScrollViewDelegate {
/// Used to update any Accessibility properties.
open override func updateAccessibility() {
super.updateAccessibility()
primaryAccessibilityElement.accessibilityHint = "Double tap on the \(tooltipModel.closeButtonText) button to close."
primaryAccessibilityElement.accessibilityFrameInContainerSpace = .init(origin: .zero, size: frame.size)
}
var elements: [Any] = [primaryAccessibilityElement]
contentStackView.arrangedSubviews.forEach{ elements.append($0) }
elements.append(closeButton)
open override var accessibilityElements: [Any]? {
get {
var elements: [Any] = [primaryAccessibilityElement]
contentStackView.arrangedSubviews.forEach{ elements.append($0) }
elements.append(closeButton)
accessibilityElements = elements
return elements
}
set {}
}
}

View File

@ -6,3 +6,90 @@
//
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
// }
}
}
}

View File

@ -9,6 +9,4 @@ import Foundation
public protocol Groupable: Control {
/// Property used to add context to the Grouping of a set.
var accessibilityValueText: String? { get set }
}

View File

@ -9,16 +9,13 @@ import Foundation
import UIKit
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.
var subscribers: Set<AnyCancellable> { get set }
/// Key of whether or not updateView() is called in setNeedsUpdate()
var shouldUpdateView: Bool { get set }
/// Key of whether or not updateAccessibility() is called in setNeedsUpdate()
var shouldUpdateAccessibility: Bool { get set }
/// Used for setting an implementation for the default Accessible Action
var accessibilityAction: ((Self) -> Void)? { get set }
@ -42,9 +39,7 @@ extension ViewProtocol {
if shouldUpdateView {
shouldUpdateView = false
updateView()
if shouldUpdateAccessibility {
updateAccessibility()
}
updateAccessibility()
shouldUpdateView = true
}
}
@ -70,86 +65,3 @@ extension ViewProtocol where Self: UIControl {
}).store(in: &subscribers)
}
}
public protocol AccessibilityUpdatable {
// Basic accessibility
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 }
}
extension NSObject: AccessibilityUpdatable {
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
public var bridge_isAccessibilityElementBlock: AXBoolReturnBlock? {
get {
return objc_getAssociatedObject(self, &NSObject.isAccessibilityElementBlockKey) as? AXBoolReturnBlock
}
set {
objc_setAssociatedObject(self, &NSObject.isAccessibilityElementBlockKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
// if #available(iOS 17, *) {
// self.isAccessibilityElementBlock = newValue
// }
}
}
public var bridge_accessibilityActivateBlock: AXBoolReturnBlock? {
get {
return objc_getAssociatedObject(self, &NSObject.activateBlockKey) as? AXBoolReturnBlock
}
set {
objc_setAssociatedObject(self, &NSObject.activateBlockKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
// if #available(iOS 17, *) {
// self.accessibilityActivateBlock = newValue
// }
}
}
public var bridge_accessibilityValueBlock: AXStringReturnBlock? {
get {
return objc_getAssociatedObject(self, &NSObject.valueBlockKey) as? AXStringReturnBlock
}
set {
objc_setAssociatedObject(self, &NSObject.valueBlockKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
// if #available(iOS 17, *) {
// self.accessibilityValueBlock = newValue
// }
}
}
public var bridge_accessibilityHintBlock: AXStringReturnBlock? {
get {
return objc_getAssociatedObject(self, &NSObject.hintBlockKey) as? AXStringReturnBlock
}
set {
objc_setAssociatedObject(self, &NSObject.hintBlockKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
// if #available(iOS 17, *) {
// self.accessibilityHintBlock = newValue
// }
}
}
public var bridge_accessibilityLabelBlock: AXStringReturnBlock? {
get {
return objc_getAssociatedObject(self, &NSObject.labelBlockKey) as? AXStringReturnBlock
}
set {
objc_setAssociatedObject(self, &NSObject.labelBlockKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
// if #available(iOS 17, *) {
// self.accessibilityLabelBlock = newValue
// }
}
}
}