more code refactoring
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
This commit is contained in:
parent
d8c3ba2e33
commit
73f27d1e8b
@ -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
|
||||
|
||||
@ -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.
|
||||
@ -119,13 +129,6 @@ open class SelectorBase: Control, SelectorControlable {
|
||||
setNeedsLayout()
|
||||
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() { }
|
||||
|
||||
@ -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,15 +169,48 @@ 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.
|
||||
open override func setup() {
|
||||
super.setup()
|
||||
|
||||
selectorView.isAccessibilityElement = true
|
||||
selectorView.shouldUpdateAccessibility = false
|
||||
|
||||
selectorView.isAccessibilityElement = true
|
||||
isAccessibilityElement = false
|
||||
addSubview(mainStackView)
|
||||
|
||||
@ -235,14 +238,6 @@ open class SelectorItemBase<Selector: SelectorBase>: Control, Errorable, Changea
|
||||
selectorView.isEnabled = isEnabled
|
||||
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 {
|
||||
|
||||
@ -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()
|
||||
|
||||
}
|
||||
// }
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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()
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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)" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,7 +30,7 @@ open class ButtonIcon: Control, Changeable {
|
||||
public required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Enums
|
||||
//--------------------------------------------------
|
||||
@ -43,7 +43,7 @@ open class ButtonIcon: Control, Changeable {
|
||||
public enum SurfaceType: String, CaseIterable {
|
||||
case colorFill, media
|
||||
}
|
||||
|
||||
|
||||
/// Enum used to describe the size of button icon.
|
||||
public enum Size: String, EnumSubset {
|
||||
case large
|
||||
@ -105,12 +105,12 @@ open class ButtonIcon: Control, Changeable {
|
||||
return .init(x: 6, y: 6)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Public Properties
|
||||
//--------------------------------------------------
|
||||
public var onChangeSubscriber: AnyCancellable?
|
||||
|
||||
|
||||
///Badge Indicator object used to render for the ButtonIcon.
|
||||
open var badgeIndicator = BadgeIndicator().with {
|
||||
$0.translatesAutoresizingMaskIntoConstraints = false
|
||||
@ -140,10 +140,10 @@ open class ButtonIcon: Control, Changeable {
|
||||
open var selectedIconName: Icon.Name? { didSet { setNeedsUpdate() } }
|
||||
|
||||
open var selectedIconColorConfiguration: SurfaceColorConfiguration? { didSet { setNeedsUpdate() } }
|
||||
|
||||
|
||||
/// Sets the size of button icon and icon.
|
||||
open var size: Size = .large { didSet { setNeedsUpdate() } }
|
||||
|
||||
|
||||
/// If provided, the button icon will have a box shadow.
|
||||
open var floating: Bool = false { didSet { setNeedsUpdate() } }
|
||||
|
||||
@ -152,7 +152,7 @@ open class ButtonIcon: Control, Changeable {
|
||||
|
||||
/// If set to true, the button icon will not have a border.
|
||||
open var hideBorder: Bool = true { didSet { setNeedsUpdate() } }
|
||||
|
||||
|
||||
/// If provided, the badge indicator will present.
|
||||
open var showBadgeIndicator: Bool = false { didSet { setNeedsUpdate() } }
|
||||
|
||||
@ -169,14 +169,14 @@ open class ButtonIcon: Control, Changeable {
|
||||
|
||||
/// Used to move the icon inside the button in both x and y axis.
|
||||
open var iconOffset: CGPoint = .init(x: 0, y: 0) { didSet { setNeedsUpdate() } }
|
||||
|
||||
|
||||
|
||||
/// Sets a custom size of button icon container.
|
||||
open var customContainerSize: Int? { didSet { setNeedsUpdate() } }
|
||||
|
||||
|
||||
/// Sets a custom size of the icon.
|
||||
open var customIconSize: Int? { didSet { setNeedsUpdate() } }
|
||||
|
||||
|
||||
/// Sets a custom badgeIndicator offset
|
||||
open var customBadgeIndicatorOffset: CGPoint? { didSet { setNeedsUpdate() } }
|
||||
|
||||
@ -246,7 +246,7 @@ open class ButtonIcon: Control, Changeable {
|
||||
SurfaceColorConfiguration(.clear, .clear).eraseToAnyColorable()
|
||||
}()
|
||||
}
|
||||
|
||||
|
||||
private struct LowContrastColorFillConfiguration: Configuration {
|
||||
var kind: Kind = .lowContrast
|
||||
var surfaceType: SurfaceType = .colorFill
|
||||
@ -255,7 +255,7 @@ open class ButtonIcon: Control, Changeable {
|
||||
SurfaceColorConfiguration(VDSColor.paletteGray44.withAlphaComponent(0.06), VDSColor.paletteGray44.withAlphaComponent(0.26)).eraseToAnyColorable()
|
||||
}()
|
||||
}
|
||||
|
||||
|
||||
private struct LowContrastColorFillFloatingConfiguration: Configuration, DropShadowableConfiguration {
|
||||
var kind: Kind = .lowContrast
|
||||
var surfaceType: SurfaceType = .colorFill
|
||||
@ -277,7 +277,7 @@ open class ButtonIcon: Control, Changeable {
|
||||
}
|
||||
var configurations: [DropShadowable] { [dropshadow1Configuration, dropshadow2Configuration] }
|
||||
}
|
||||
|
||||
|
||||
private struct LowContrastMediaConfiguration: Configuration, Borderable {
|
||||
var kind: Kind = .lowContrast
|
||||
var surfaceType: SurfaceType = .media
|
||||
@ -290,7 +290,7 @@ open class ButtonIcon: Control, Changeable {
|
||||
SurfaceColorConfiguration(VDSColor.elementsLowcontrastOnlight, VDSColor.elementsLowcontrastOndark).eraseToAnyColorable()
|
||||
}()
|
||||
}
|
||||
|
||||
|
||||
private struct LowContrastMediaFloatingConfiguration: Configuration, DropShadowableConfiguration {
|
||||
var kind: Kind = .lowContrast
|
||||
var surfaceType: SurfaceType = .media
|
||||
@ -325,10 +325,10 @@ open class ButtonIcon: Control, Changeable {
|
||||
$0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: .disabled)
|
||||
$0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: [.selected, .disabled])
|
||||
}.eraseToAnyColorable()
|
||||
|
||||
|
||||
}()
|
||||
}
|
||||
|
||||
|
||||
private struct HighContrastFloatingConfiguration: Configuration, DropShadowableConfiguration {
|
||||
var kind: Kind = .highContrast
|
||||
var surfaceType: SurfaceType = .colorFill
|
||||
@ -357,9 +357,9 @@ open class ButtonIcon: Control, Changeable {
|
||||
}
|
||||
var configurations: [DropShadowable] { [dropshadow1Configuration, dropshadow2Configuration] }
|
||||
}
|
||||
|
||||
|
||||
private var badgeIndicatorDefaultSize: CGSize = .zero
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Overrides
|
||||
//--------------------------------------------------
|
||||
@ -367,11 +367,11 @@ open class ButtonIcon: Control, Changeable {
|
||||
open override func setup() {
|
||||
super.setup()
|
||||
isAccessibilityElement = false
|
||||
|
||||
|
||||
//create a layoutGuide for the icon to key off of
|
||||
let iconLayoutGuide = UILayoutGuide()
|
||||
addLayoutGuide(iconLayoutGuide)
|
||||
|
||||
|
||||
//add the icon
|
||||
addSubview(icon)
|
||||
|
||||
@ -379,7 +379,7 @@ open class ButtonIcon: Control, Changeable {
|
||||
addSubview(badgeIndicator)
|
||||
badgeIndicator.isHidden = !showBadgeIndicator
|
||||
badgeIndicatorDefaultSize = badgeIndicator.frame.size
|
||||
|
||||
|
||||
//determines the height/width of the icon
|
||||
layoutGuideWidthConstraint = iconLayoutGuide.width(constant: size.containerSize)
|
||||
layoutGuideHeightConstraint = iconLayoutGuide.height(constant: size.containerSize)
|
||||
@ -388,7 +388,7 @@ open class ButtonIcon: Control, Changeable {
|
||||
badgeIndicatorCenterXConstraint = badgeIndicator.centerXAnchor.constraint(equalTo: icon.centerXAnchor)
|
||||
badgeIndicatorCenterYConstraint = icon.centerYAnchor.constraint(equalTo: badgeIndicator.centerYAnchor)
|
||||
badgeIndicatorCenterYConstraint?.isActive = true
|
||||
|
||||
|
||||
badgeIndicatorLeadingConstraint?.isActive = true
|
||||
//pin layout guide
|
||||
iconLayoutGuide
|
||||
@ -396,7 +396,7 @@ open class ButtonIcon: Control, Changeable {
|
||||
.pinLeading()
|
||||
.pinTrailing(0, .defaultHigh)
|
||||
.pinBottom(0, .defaultHigh)
|
||||
|
||||
|
||||
//determines the center point of the icon
|
||||
centerXConstraint = icon.centerXAnchor.constraint(equalTo: iconLayoutGuide.centerXAnchor, constant: 0)
|
||||
centerXConstraint?.activate()
|
||||
@ -414,14 +414,14 @@ open class ButtonIcon: Control, Changeable {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// This will change the state of the Selector and execute the actionBlock if provided.
|
||||
open func toggle() {
|
||||
//removed error
|
||||
isSelected.toggle()
|
||||
sendActions(for: .valueChanged)
|
||||
}
|
||||
|
||||
|
||||
/// Resets to default settings.
|
||||
open override func reset() {
|
||||
super.reset()
|
||||
@ -437,7 +437,7 @@ open class ButtonIcon: Control, Changeable {
|
||||
showBadgeIndicator = false
|
||||
selectable = false
|
||||
badgeIndicatorModel = nil
|
||||
onChange = nil
|
||||
onChange = nil
|
||||
shouldUpdateView = true
|
||||
setNeedsUpdate()
|
||||
}
|
||||
@ -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() {
|
||||
|
||||
@ -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.
|
||||
@ -118,12 +124,7 @@ open class Icon: View {
|
||||
super.reset()
|
||||
color = VDSColor.paletteBlack
|
||||
imageView.image = nil
|
||||
}
|
||||
|
||||
open override func updateAccessibility() {
|
||||
super.updateAccessibility()
|
||||
accessibilityLabel = name?.rawValue ?? "icon"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension UIImage {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,12 +125,10 @@ 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 hiddenValue: String? { didSet { setNeedsUpdate() } }
|
||||
|
||||
open var accessibilityValueText: String?
|
||||
|
||||
open override var accessibilityAction: ((Control) -> Void)? {
|
||||
didSet {
|
||||
selectorView.accessibilityAction = { [weak self] selectorItemBase in
|
||||
@ -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,15 +171,39 @@ open class RadioBoxItem: Control, Changeable, FormFieldable, Groupable {
|
||||
control.toggle()
|
||||
}
|
||||
|
||||
if #available(iOS 17.0, *) {
|
||||
accessibilityHintBlock = { [weak self] in
|
||||
|
||||
return "foo"
|
||||
selectorView.bridge_accessibilityLabelBlock = { [weak self] in
|
||||
guard let self else { return "" }
|
||||
var accessibilityLabels = [String]()
|
||||
|
||||
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: ", ")
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations.
|
||||
@ -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]()
|
||||
|
||||
@ -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)" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -88,8 +88,6 @@ extension Tabs {
|
||||
open var minWidth: CGFloat = 44.0 { didSet { setNeedsUpdate() } }
|
||||
|
||||
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()
|
||||
|
||||
|
||||
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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"
|
||||
|
||||
//--------------------------------------------------
|
||||
@ -283,11 +259,11 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable {
|
||||
.pinBottom()
|
||||
|
||||
trailingEqualsConstraint = layoutGuide.pinTrailing(anchor: trailingAnchor)
|
||||
|
||||
|
||||
// width constraints
|
||||
trailingLessThanEqualsConstraint = layoutGuide.pinTrailingLessThanOrEqualTo(anchor: trailingAnchor)?.deactivate()
|
||||
widthConstraint = layoutGuide.widthAnchor.constraint(equalToConstant: 0).deactivate()
|
||||
|
||||
|
||||
// Add mainStackView to the view
|
||||
addSubview(mainStackView)
|
||||
|
||||
@ -301,7 +277,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable {
|
||||
//InputContainer, Icons, Buttons
|
||||
containerView.addSubview(fieldStackView)
|
||||
fieldStackView.pinToSuperView(.uniform(VDSLayout.space3X))
|
||||
|
||||
|
||||
let fieldContainerView = getFieldContainer()
|
||||
fieldContainerView.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
@ -309,11 +285,11 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable {
|
||||
fieldStackView.addArrangedSubview(fieldContainerView)
|
||||
fieldStackView.addArrangedSubview(statusIcon)
|
||||
fieldStackView.setCustomSpacing(VDSLayout.space3X, after: fieldContainerView)
|
||||
|
||||
|
||||
//get the container this is what show helper text, error text
|
||||
//can include other for character count, max length
|
||||
bottomContainerView = getBottomContainer()
|
||||
|
||||
|
||||
//this is the vertical stack that contains error text, helper text
|
||||
bottomContainerStackView.addArrangedSubview(errorLabel)
|
||||
bottomContainerStackView.addArrangedSubview(helperLabel)
|
||||
@ -321,11 +297,11 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable {
|
||||
// Add arranged subviews to textFieldStackView
|
||||
contentStackView.addArrangedSubview(containerView)
|
||||
contentStackView.addArrangedSubview(bottomContainerView)
|
||||
|
||||
|
||||
// Add arranged subviews to mainStackView
|
||||
mainStackView.addArrangedSubview(titleLabel)
|
||||
mainStackView.addArrangedSubview(contentStackView)
|
||||
|
||||
|
||||
// Initial position of the helper label
|
||||
updateHelperTextPosition()
|
||||
|
||||
@ -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]()
|
||||
|
||||
@ -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() } }
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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]()
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
@ -238,16 +246,6 @@ open class Toggle: Control, Changeable, FormFieldable {
|
||||
toggleView.isEnabled = isEnabled
|
||||
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() {
|
||||
|
||||
@ -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.
|
||||
@ -176,13 +176,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() {
|
||||
|
||||
@ -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.
|
||||
@ -163,23 +181,7 @@ open class Tooltip: Control, TooltipLaunchable {
|
||||
//get the color for the image
|
||||
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 {
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
open override var accessibilityElements: [Any]? {
|
||||
get {
|
||||
var elements: [Any] = [primaryAccessibilityElement]
|
||||
contentStackView.arrangedSubviews.forEach{ elements.append($0) }
|
||||
elements.append(closeButton)
|
||||
|
||||
var elements: [Any] = [primaryAccessibilityElement]
|
||||
contentStackView.arrangedSubviews.forEach{ elements.append($0) }
|
||||
elements.append(closeButton)
|
||||
|
||||
accessibilityElements = elements
|
||||
return elements
|
||||
}
|
||||
set {}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -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 }
|
||||
}
|
||||
|
||||
@ -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
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user