Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I make an arbitrary UIAccessibilityElement behave like a UISwitch for VoiceOver?

When selecting a native switch with VoiceOver, the announcement will contain "Off" or "On" with an additional hint "double tap to toggle setting".

I have tried using the accessibility trait UIAccessibilityTraitSelected, but that only results in "Selected" being announced, with no hint unless I provide one explicitly.

Using the Accessibility Inspector I've also noticed that native UIKit switches have an accessibilityValue of 1 when enabled, but providing that does not change VoiceOver behavior.

- (UIAccessibilityTraits)accessibilityTraits {
  if (toggled) {
    return UIAccessibilityTraitSelected;
  } else {
    return UIAccessibilityTraitNone;
  }
}

- (NSString*)accessibilityValue {
  if (toggled) {
    return @"1";
  } else {
    return @"0"
  }
}

Is it possible to provide some combination of traits/value/label such that TalkBack recognizes this element as a Switch, without using a UISwitch?

like image 607
Jonah Williams Avatar asked Sep 07 '18 23:09

Jonah Williams


2 Answers

I have created an accessible view that acts like a switch here.

The only way that I have been able to get any arbitrary element to act like a Switch is when inheriting the UIAccessibilityTraits of a Switch. This causes VoiceOver to read the Accessibility Value (0 or 1) as "Off" or "On," adds the hint "Double tap to toggle setting", and makes VoiceOver say "Switch Button."

You could potentially do this by overriding the view's Accessibility Traits like so:

override var accessibilityTraits(): UIAccessibilityTraits {
    get { return UISwitch().accessibilityTraits }
    set {}
}

Hope this helps!

like image 161
jldailey Avatar answered Sep 20 '22 17:09

jldailey


You can create a custom accessibility element behaving like a UISwitchControl with whatever you want. The only thing to be specified is the way VoiceOver should interpret it.

Let's suppose you want to gather a label and a view to be seen as a switch control.

First of all, create a class for grouping these elements into a single one :

class WrapView: UIView {

static let defaultValue = "on"

override init(frame: CGRect) {
    super.init(frame: frame)
}


required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
}


convenience init(with label: UILabel,and view: UIView) {

    let viewFrame = label.frame.union(view.frame)
    self.init(frame: viewFrame)

    self.isAccessibilityElement = true
    self.accessibilityLabel = label.accessibilityLabel
    self.accessibilityValue = WrapView.defaultValue
    self.accessibilityHint = "element is" + self.accessibilityValue! + ", tap twice to change the status."
}
}

Then, just create your custom view in your viewDidAppear() :

class ViewController: UIViewController {

@IBOutlet weak var myView: UIView!
@IBOutlet weak var myLabel: UILabel!


override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    let myCustomView = WrapView.init(with: myLabel, and: myView)

    self.view.addSubview(myCustomView)
}
}

Finally, to have a custom view behaving like a switch control, just override the accessibilityActivate function in your WrapView class to implement your logic when your view is double tapped :

override func accessibilityActivate() -> Bool {

    self.accessibilityValue = (self.accessibilityValue == WrapView.defaultValue) ? "off" : "on"
    self.accessibilityHint = "element is" + self.accessibilityValue! + ", tap twice to change the status."

    return true
}

And now you have a custom element that contains whatever you want and that behaves like a switch control for blind people using VoiceOver without using a UISwitch as you wanted.

like image 27
XLE_22 Avatar answered Sep 16 '22 17:09

XLE_22