Correct implementation of VoiceOver accessibility in iOS with `accessibilityLabel` and `accessibilityHint` - ios

I'm trying to optimize my app. What is the best practice method for implementing accessibility in iOS with accessibilityLabel and accessibilityHint? Are accessibility labels always required?
My app is very dynamic and objects in the view get updated frequently depending on the user's action. For example, a view might start off as one shape and later turn into another shape. Accordingly, the accessibilityLabel and accessibilityHint of each object is updated frequently to reflect the changed view for users with VoiceOver turned on.
Questions
Is it safe to expect that when VoiceOver is not running (i.e. !UIAccessibilityIsVoiceOverRunning()) then setting
accessibilityLabel and accessibilityHint is completely
unnecessary?
Are there assistive technologies other than VoiceOver that a user might use that access accessibilityLabel and accessibilityHint?

What is the best practice method for implementing accessibility in iOS with accessibilityLabel and accessibilityHint?
You should consider, rather than "setting" the accessibility label, overriding the property implementation. Allow the view that requires an accessibility label to calculate its value when required, rather than keeping the value in sync constantly. Much easier! You would do so with code that looks like this.
override public var accessibilityLabel: String? {
get {
return "Calculated label"
}
set {
//Specifically do nothing. We're not "setting a property" we're responding to changes of the internal views.
}
}
Are accessibility labels always required?
Yes and no. Accessibility labels are always required for elements that present information and are individually focusable (you may have a group f controls, wrapped in one layout, with one accessibility label). I can say with reasonably certainty that in the context of your question, removing the accessibility label is absolutely the incorrect thing to do.
Is it safe to expect that when VoiceOver is not running (i.e.
!UIAccessibilityIsVoiceOverRunning()) then setting accessibilityLabel
and accessibilityHint is completely unnecessary?
No, there are other uses for accessibility properties outside of VoiceOver.
Are there assistive technologies other than VoiceOver that a user
might use that access accessibilityLabel and accessibilityHint?
There are certainly assistive technologies outside of VoiceOver, and they are dependent on a a myriad of accessibility properties. (braille boards, switch access, etc)
Conclusion
It seems to me like you are trying to get around accessibility as a software practice and rationalize doing so. This is the wrong thought process. HOWEVER, it may indeed be impractical to make the view that you have accessible because of some fundamental design problem. You should consider whether or not the thing you have is designed in a way that it can be made accessible from a development/API point of view. The APIs do limit you from accomplishing certain things. Whether or not your up against one of these limitations is NOT a question you can ask on StackOverflow and the solution is NOT to omit accessibility information. It is to redesign the control OR perhaps to provide an alternate accessible implementation... though this route should be considered VERY carefully. In general separate is not equal.

Related

UITextInput not announced correctly by VoiceOver

I have a problem with how VoiceOver announces my custom UITextInput while it has the keyboard focus: When the accessibility focus is moved to the UITextInput view by swiping left/right the UITextInput is correctly announced by VoiceOver and I am hearing something like Text field, is editing, <content of text input>, character mode, insertion point at end.
However, if I move the accessibility focus to the UITextInput by tapping on it, VoiceOver says empty line, which is not correct.
I would expect VoiceOver to make the exact same announcement regardless of how the UITextInput got the accessibility focus.
Any ideas what might be the cause of this strange behavior?
To mark up a text field you need to take advantage of a few different properties. First, the "Text field" part you can only get by having your custom control extend the UITextFIeld class. That's the only way you can get that announcement at the beginning without effecting the read out of other things. You could append this to the label. Here are the properties I would recommend for your custom text editor.
Preferred Markup
ClassType: Subclass of UITextField (Probably the most important piece)
AccessibilityLabel: The thing the entered text represents (Ex: Password, Username, etc).
AccessibilityValue: The entered text.
AccessibilityHint: "Some non critical information, that shares some details about what the entered information will be used for."
AccessibilityTraits: (NOT Static Text Trait)
Note that the hint isn't crucial information. Hints are frequently ignored, and just contain useful clarifying information.
Aleternate markup (NOT RECOMMENDED)
ClassType: Subclass of ????
AccessibilityLabel: Text field, $EditingState, $EnteredText, $InsertionPoint
AccessibilityValue: nil
AccessibilityHint: nil
AccessibilityTraits: StaticText (Weird, but if you're including role information yourself, we want the trait of static text on an editable text field, to avoid VoiceOver being smart, including it, and duplicating role information in the announcement... is this starting to feel like a hack yet???)
This is honestly a terrible idea, you really should just have your TextField inherit from the system UITextField and get all of that stuff for free, but if you must, this is how you wold achieve... similar behavior without doing so. I say similar because there are a few things you CANNOT replicate in a custom control.
keyboard type ("character mode") is more painful for you to grab than the system, and I recommend omitting in the custom case. The keyboard doesn't have to respect your requests for mode changes (custom keyboards and such), you can get into trouble and confuse users attempting to share this based on your application's settings. The system is the only thing that can get this right in all scenarios.
Inflection. By doing this the custom way you will lose the style and inflection from VoiceOver. The pauses between the Label and the Entered Text. The lower voice that is read when the placeholder text is read out, etc. You can add commas to your AccessibilityLabel to somewhat replicate some of this, but ultimately, your custom control will always sound a little "off" to a VoiceOver user... unless your custom view extends UITextField...

iOS Accessibility - Change AccessibilityElementsHidden without delay?

I am working on a view in which certain elements will be removed and re-added as accessible items depending on the state of the view. I have been able to successfully achieve the functionality I desire by setting AccessibilityElementsHidden to toggle the state.
However, I am finding that there is a brief pause (~1-2 seconds) between this field being set before the Accessibility Layout is updated, which can allow the user to highlight a deactivated accessibility element if they are moving at a reasonable pace. If they are focused on an item as it is being disabled it makes it difficult to re-orient oneself in the VoiceOver interface.
I have found methods to immediately update the display of the interface (by means of SetNeedsLayout() and LayoutIfNeeded() on the main thread) but unfortunately this does not trigger the Accessibility Layout update.
I have also tried using UIAccessibilityPostNotification.LayoutChanged but like the changing of the AccessibilityElementsHidden property, this also takes a moment to propagate to the view.
Can anyone provide some insight as to what I need to do to ensure the user cannot put themselves in a bad state before the Accessible Layout is applied?

Seeking advice for "dual mode" for increment/decrement of control with UIAccessibilityTraitAdjustable

I implemented a custom iOS control (looking similar to a ruler), where the user can easily scroll to select one of about 300 values.
My control also has the trait UIAccessibilityTraitAdjustable and implements the methods accessibilityIncrement() and accessibilityDecrement() of the protocol UIAccessibilityAction to update its value. So far everything works as intended.
The problem is that adjusting the value via VoiceOver can be tedious, since each swipe gesture increments/decrements the value only by one. Is there a good way to offer a second mode where the updates happen, e.g. in steps of ten?
UIAccessibilityTraitAdjustable uses a fixed, developer-defined increment size. You can adjust the slider with a larger step size in -accessibilityIncrement if you think users will find it tedious to adjust the control one unit at a time. VoiceOver users desiring finer control can use the pass-through gesture (double tap and hold) to interact with the control directly, sliding to adjust it in single-percent increments. This does, however, assume users have fine motor control.
If you feel strongly that UIAccessibility should support a "fine-grain" adjustment mode, file an enhancement request with supporting use cases.

UIAccessibility - containers

There's a "Containers" rotor option in Voiceover which allows the user to quickly navigate through "high level" sections of the screen via single finger swipe up and swipe down actions. For example, the Calendar app has three high level items: navbar, contents and toolbar.
My app uses custom UIView subclasses and, no matter what I try to do, all my views seem to belong to a single container. I can't split them into logical sections. I tried putting them in separate views implementing the UIAccessibilityContainer protocol and setting a few of the accessibility properties on the parent views.
Does anyone know how to create multiple containers?
I did some digging on this issue and think its a private trait Apple is using. First I noticed the only containers recognized are standard UIKit type objects like UITableViews, UITabBars, UINavigationBars, etc. So next I used the debugger to inspect the value of the accessibility traits for these components. They're all 0x200000000000. Just to be sure I didn't miss an UIAccessibilityTrait I checked all of their values. None of them match the value. Furthermore if you set your views accessibility traits to this mysterious value it'll work just like you want! I tried determining the location of this constant but didn't have much luck. If you want to do more digging it looks like apple stores accessibilityTraits using an NSObject category that uses associated objects with some constant value named AXTraitsIdentifier.
Practically speaking you could do something like the below but since its not defined in a public API its functionality could change in the future
//Note the navBar has to be run through a voice over pass before the value is set :( or you can just directly set the value to 0x200000000000.
myContainerView.accessibilityTraits = navBar.accessibilityTraits;
I'd love to hear if anyone one else has info on this? So far I haven't found an ideal solution.
I have been able to make the views in my app reachable by single finger swipe up and swipe down actions when the "Containers" rotor option is selected by setting the accessibilityContainerType property of my views to semanticGroup.

UIAccessibilityFocus protocol not working with UITextField

With UIAccessibilityFocus protocol, supposedly, if you override accessibilityElementDidBecomeFocused() and accessibilityElementDidLoseFocus(), you will be able to track when an accessible element gain or lose focus while Voiceover is running. This seems to work well with all field types - UIButton, UILabel, UISwitch, UITextView, etc. - except UITextField. When Voiceover focus is on (or leaving) an UITextField, those functions are simply not called. Just wondering if it is a bug or something else. Thanks!
This is a feature, let me explain.
Without VoiceOver turned on there is no concept of focus within iOS. Except in the case of UITextField. UITextFields get "focused" (again focus isn't really a concept in iOS without voiceover) with or without VoiceOver on. For the other elements, this is not the case. They do not have "gainFocus" equivalents. A UIButton gaining focus is only meaningful from an accessibility standpoint. So they add in the special accessibilityElementDidGainFocus calls for those classes. They are specifically removed from UITextFields because that call would be logically equivalent to calls that already exist for that class, independent of the Accessibility API.

Resources