Skip to content

Commit 9614f39

Browse files
committed
Check UISwitch state using combination of trait and value
1 parent 0f195fb commit 9614f39

File tree

5 files changed

+59
-49
lines changed

5 files changed

+59
-49
lines changed

Example/AccessibilitySnapshot/SwitchControlViewController.swift

+32-33
Original file line numberDiff line numberDiff line change
@@ -91,26 +91,36 @@ private extension SwitchControlViewController {
9191

9292
switchControls.forEach { addSubview($0) }
9393

94+
let switchTrait = UIAccessibilityTraits(rawValue: 0x0020000000000000)
95+
9496
// Add a fake switch that has the switch button trait only, but is not a UISwitch.
95-
fakeSwitchView.isAccessibilityElement = true
96-
fakeSwitchView.accessibilityLabel = "Fake Label"
97-
fakeSwitchView.accessibilityValue = "Value"
98-
fakeSwitchView.accessibilityTraits.insert(UIAccessibilityTraits(rawValue: 0x0020000000000000))
99-
fakeSwitchView.frame.size = .init(width: 48, height: 32)
100-
fakeSwitchView.backgroundColor = .lightGray
101-
fakeSwitchView.layer.cornerRadius = 16
102-
addSubview(fakeSwitchView)
103-
104-
// Add a fake switch that has the switch button and button traits, but is not a UISwitch.
105-
fakeSwitchButton.isAccessibilityElement = true
106-
fakeSwitchButton.accessibilityLabel = "Fake Label"
107-
fakeSwitchButton.accessibilityValue = "Value"
108-
fakeSwitchButton.accessibilityTraits.insert(.button)
109-
fakeSwitchButton.accessibilityTraits.insert(UIAccessibilityTraits(rawValue: 0x0020000000000000))
110-
fakeSwitchButton.frame.size = .init(width: 48, height: 32)
111-
fakeSwitchButton.backgroundColor = .lightGray
112-
fakeSwitchButton.layer.cornerRadius = 16
113-
addSubview(fakeSwitchButton)
97+
for fakeSwitchView in fakeSwitchViews {
98+
fakeSwitchView.isAccessibilityElement = true
99+
fakeSwitchView.accessibilityLabel = "Fake Label"
100+
fakeSwitchView.frame.size = .init(width: 48, height: 32)
101+
fakeSwitchView.backgroundColor = .lightGray
102+
fakeSwitchView.layer.cornerRadius = 16
103+
}
104+
105+
fakeSwitchViews[0].accessibilityValue = "1"
106+
fakeSwitchViews[0].accessibilityTraits = [switchTrait, .button]
107+
108+
fakeSwitchViews[1].accessibilityValue = "0"
109+
fakeSwitchViews[1].accessibilityTraits = [switchTrait, .button]
110+
111+
fakeSwitchViews[2].accessibilityValue = "2"
112+
fakeSwitchViews[2].accessibilityTraits = [switchTrait, .button]
113+
114+
fakeSwitchViews[3].accessibilityValue = "1"
115+
fakeSwitchViews[3].accessibilityTraits = [switchTrait]
116+
117+
fakeSwitchViews[4].accessibilityValue = "3"
118+
fakeSwitchViews[4].accessibilityTraits = [switchTrait]
119+
120+
fakeSwitchViews[5].accessibilityValue = "Value"
121+
fakeSwitchViews[5].accessibilityTraits = [.button, switchTrait]
122+
123+
fakeSwitchViews.forEach { addSubview($0) }
114124
}
115125

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

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

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

131138
// MARK: - UIView
132139

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

138145
var distributionSpecifiers: [ViewDistributionSpecifying] = [ statusBarHeight.fixed, 1.flexible ]
139-
for subview in switchControls {
146+
for subview in (switchControls + fakeSwitchViews) {
140147
distributionSpecifiers.append(subview)
141148
distributionSpecifiers.append(1.flexible)
142149
}
143-
distributionSpecifiers.append(
144-
contentsOf: [
145-
fakeSwitchView.distributionItem,
146-
1.flexible,
147-
fakeSwitchButton.distributionItem,
148-
1.flexible,
149-
]
150-
)
151150
applyVerticalSubviewDistribution(distributionSpecifiers)
152151
}
153152

Sources/AccessibilitySnapshot/Core/Swift/Assets/en.lproj/Localizable.strings

+2-2
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,10 @@
3535
"trait.search_field.description" = "Search Field.";
3636

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

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

4343
/* Description for the 'switch button' accessibility trait, when the state of the switch cannot be determined */
4444
"trait.switch_button.state_unspecified.description" = "Switch Button.";

Sources/AccessibilitySnapshot/Core/Swift/Classes/UIAccessibility+SnapshotAdditions.swift

+25-14
Original file line numberDiff line numberDiff line change
@@ -107,20 +107,24 @@ extension NSObject {
107107
}
108108

109109
if accessibilityTraits.contains(.switchButton) {
110-
if let `self` = self as? UISwitch {
111-
if self.isOn {
112-
traitSpecifiers.append(strings.switchButtonOnTraitName)
113-
} else {
114-
traitSpecifiers.append(strings.switchButtonOffTraitName)
115-
}
116-
117-
} else if accessibilityTraits.contains(.button) {
118-
// An element can have the private switch button trait without being a UISwitch (for example, by passing through
119-
// the traits of a contained switch). In this case, VoiceOver will still read the "Switch Button." trait, but
120-
// will not read whether or not the switch is turned on. If the element's traits do not also include the `.button`
121-
// trait, VoiceOver will not read the trait description.
110+
if accessibilityTraits.contains(.button) {
111+
// An element can have the private switch button trait without being a UISwitch (for example, by passing
112+
// through the traits of a contained switch). In this case, VoiceOver will still read the "Switch
113+
// Button." trait, but only if the element's traits also include the `.button` trait.
122114
traitSpecifiers.append(strings.switchButtonStatelessTraitName)
123115
}
116+
117+
switch accessibilityValue {
118+
case "1":
119+
traitSpecifiers.append(strings.switchButtonOnTraitName)
120+
case "0":
121+
traitSpecifiers.append(strings.switchButtonOffTraitName)
122+
case "2":
123+
traitSpecifiers.append(strings.switchButtonMixedTraitName)
124+
default:
125+
// When the switch button trait is set, unknown accessibility values are omitted from the description.
126+
break
127+
}
124128
}
125129

126130
let showsTabTraitInContext = context?.showsTabTrait ?? false
@@ -308,6 +312,8 @@ extension NSObject {
308312

309313
let switchButtonOffTraitName: String
310314

315+
let switchButtonMixedTraitName: String
316+
311317
let switchButtonStatelessTraitName: String
312318

313319
let switchButtonTraitHint: String
@@ -396,16 +402,21 @@ extension NSObject {
396402
comment: "Description for the 'search field' accessibility trait",
397403
locale: locale
398404
)
399-
self.switchButtonOnTraitName = "Switch Button. On.".localized(
405+
self.switchButtonOnTraitName = "On.".localized(
400406
key: "trait.switch_button.state_on.description",
401407
comment: "Description for the 'switch button' accessibility trait, when the switch is on",
402408
locale: locale
403409
)
404-
self.switchButtonOffTraitName = "Switch Button. Off.".localized(
410+
self.switchButtonOffTraitName = "Off.".localized(
405411
key: "trait.switch_button.state_off.description",
406412
comment: "Description for the 'switch button' accessibility trait, when the switch is off",
407413
locale: locale
408414
)
415+
self.switchButtonMixedTraitName = "Mixed.".localized(
416+
key: "trait.switch_button.state_mixed.description",
417+
comment: "Description for the 'switch button' accessibility trait, when the switch is in a mixed state",
418+
locale: locale
419+
)
409420
self.switchButtonStatelessTraitName = "Switch Button.".localized(
410421
key: "trait.switch_button.state_unspecified.description",
411422
comment: "Description for the 'switch button' accessibility trait, when the state of the switch cannot be determined",

0 commit comments

Comments
 (0)