Skip to content

Commit

Permalink
Check UISwitch state using combination of trait and value
Browse files Browse the repository at this point in the history
  • Loading branch information
NickEntin committed Nov 15, 2023
1 parent 0f195fb commit 9614f39
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 49 deletions.
65 changes: 32 additions & 33 deletions Example/AccessibilitySnapshot/SwitchControlViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,26 +91,36 @@ private extension SwitchControlViewController {

switchControls.forEach { addSubview($0) }

let switchTrait = UIAccessibilityTraits(rawValue: 0x0020000000000000)

// Add a fake switch that has the switch button trait only, but is not a UISwitch.
fakeSwitchView.isAccessibilityElement = true
fakeSwitchView.accessibilityLabel = "Fake Label"
fakeSwitchView.accessibilityValue = "Value"
fakeSwitchView.accessibilityTraits.insert(UIAccessibilityTraits(rawValue: 0x0020000000000000))
fakeSwitchView.frame.size = .init(width: 48, height: 32)
fakeSwitchView.backgroundColor = .lightGray
fakeSwitchView.layer.cornerRadius = 16
addSubview(fakeSwitchView)

// Add a fake switch that has the switch button and button traits, but is not a UISwitch.
fakeSwitchButton.isAccessibilityElement = true
fakeSwitchButton.accessibilityLabel = "Fake Label"
fakeSwitchButton.accessibilityValue = "Value"
fakeSwitchButton.accessibilityTraits.insert(.button)
fakeSwitchButton.accessibilityTraits.insert(UIAccessibilityTraits(rawValue: 0x0020000000000000))
fakeSwitchButton.frame.size = .init(width: 48, height: 32)
fakeSwitchButton.backgroundColor = .lightGray
fakeSwitchButton.layer.cornerRadius = 16
addSubview(fakeSwitchButton)
for fakeSwitchView in fakeSwitchViews {
fakeSwitchView.isAccessibilityElement = true
fakeSwitchView.accessibilityLabel = "Fake Label"
fakeSwitchView.frame.size = .init(width: 48, height: 32)
fakeSwitchView.backgroundColor = .lightGray
fakeSwitchView.layer.cornerRadius = 16
}

fakeSwitchViews[0].accessibilityValue = "1"
fakeSwitchViews[0].accessibilityTraits = [switchTrait, .button]

fakeSwitchViews[1].accessibilityValue = "0"
fakeSwitchViews[1].accessibilityTraits = [switchTrait, .button]

fakeSwitchViews[2].accessibilityValue = "2"
fakeSwitchViews[2].accessibilityTraits = [switchTrait, .button]

fakeSwitchViews[3].accessibilityValue = "1"
fakeSwitchViews[3].accessibilityTraits = [switchTrait]

fakeSwitchViews[4].accessibilityValue = "3"
fakeSwitchViews[4].accessibilityTraits = [switchTrait]

fakeSwitchViews[5].accessibilityValue = "Value"
fakeSwitchViews[5].accessibilityTraits = [.button, switchTrait]

fakeSwitchViews.forEach { addSubview($0) }
}

@available(*, unavailable)
Expand All @@ -122,11 +132,8 @@ private extension SwitchControlViewController {

private let switchControls: [UISwitch] = (0..<9).map { _ in UISwitch() }

/// UIView with the switch button trait that acts like a switch, but is not a UISwitch.
private let fakeSwitchView: UIView = .init()

/// UIView with the button and switch button traits that acts like a switch, but is not a UISwitch.
private let fakeSwitchButton: UIView = .init()
/// `UIView`s with the switch button trait that act like a switch, but aren't actually switches.
private let fakeSwitchViews: [UIView] = (0..<6).map { _ in UIView() }

// MARK: - UIView

Expand All @@ -136,18 +143,10 @@ private extension SwitchControlViewController {
let statusBarHeight = window?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0

var distributionSpecifiers: [ViewDistributionSpecifying] = [ statusBarHeight.fixed, 1.flexible ]
for subview in switchControls {
for subview in (switchControls + fakeSwitchViews) {
distributionSpecifiers.append(subview)
distributionSpecifiers.append(1.flexible)
}
distributionSpecifiers.append(
contentsOf: [
fakeSwitchView.distributionItem,
1.flexible,
fakeSwitchButton.distributionItem,
1.flexible,
]
)
applyVerticalSubviewDistribution(distributionSpecifiers)
}

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@
"trait.search_field.description" = "Search Field.";

/* Description for the 'switch button' accessibility trait, when the switch is on */
"trait.switch_button.state_on.description" = "Switch Button. On.";
"trait.switch_button.state_on.description" = "On.";

/* Description for the 'switch button' accessibility trait, when the switch is off */
"trait.switch_button.state_off.description" = "Switch Button. Off.";
"trait.switch_button.state_off.description" = "Off.";

/* Description for the 'switch button' accessibility trait, when the state of the switch cannot be determined */
"trait.switch_button.state_unspecified.description" = "Switch Button.";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,20 +107,24 @@ extension NSObject {
}

if accessibilityTraits.contains(.switchButton) {
if let `self` = self as? UISwitch {
if self.isOn {
traitSpecifiers.append(strings.switchButtonOnTraitName)
} else {
traitSpecifiers.append(strings.switchButtonOffTraitName)
}

} else if accessibilityTraits.contains(.button) {
// An element can have the private switch button trait without being a UISwitch (for example, by passing through
// the traits of a contained switch). In this case, VoiceOver will still read the "Switch Button." trait, but
// will not read whether or not the switch is turned on. If the element's traits do not also include the `.button`
// trait, VoiceOver will not read the trait description.
if accessibilityTraits.contains(.button) {
// An element can have the private switch button trait without being a UISwitch (for example, by passing
// through the traits of a contained switch). In this case, VoiceOver will still read the "Switch
// Button." trait, but only if the element's traits also include the `.button` trait.
traitSpecifiers.append(strings.switchButtonStatelessTraitName)
}

switch accessibilityValue {
case "1":
traitSpecifiers.append(strings.switchButtonOnTraitName)
case "0":
traitSpecifiers.append(strings.switchButtonOffTraitName)
case "2":
traitSpecifiers.append(strings.switchButtonMixedTraitName)
default:
// When the switch button trait is set, unknown accessibility values are omitted from the description.
break
}
}

let showsTabTraitInContext = context?.showsTabTrait ?? false
Expand Down Expand Up @@ -308,6 +312,8 @@ extension NSObject {

let switchButtonOffTraitName: String

let switchButtonMixedTraitName: String

let switchButtonStatelessTraitName: String

let switchButtonTraitHint: String
Expand Down Expand Up @@ -396,16 +402,21 @@ extension NSObject {
comment: "Description for the 'search field' accessibility trait",
locale: locale
)
self.switchButtonOnTraitName = "Switch Button. On.".localized(
self.switchButtonOnTraitName = "On.".localized(
key: "trait.switch_button.state_on.description",
comment: "Description for the 'switch button' accessibility trait, when the switch is on",
locale: locale
)
self.switchButtonOffTraitName = "Switch Button. Off.".localized(
self.switchButtonOffTraitName = "Off.".localized(
key: "trait.switch_button.state_off.description",
comment: "Description for the 'switch button' accessibility trait, when the switch is off",
locale: locale
)
self.switchButtonMixedTraitName = "Mixed.".localized(
key: "trait.switch_button.state_mixed.description",
comment: "Description for the 'switch button' accessibility trait, when the switch is in a mixed state",
locale: locale
)
self.switchButtonStatelessTraitName = "Switch Button.".localized(
key: "trait.switch_button.state_unspecified.description",
comment: "Description for the 'switch button' accessibility trait, when the state of the switch cannot be determined",
Expand Down

0 comments on commit 9614f39

Please sign in to comment.