How to get VoiceOver to announce section labels in iOS? - ios

In the iPhone Weather App, when using VoiceOver, I noticed that tapping into a section for the first time, it will announce the section.
For example, in iOS 9, tapping on any item the middle strip for the first time will announce "Hourly forecasts" before continuing to describe the element you tapped on. Tapping anything else in the strip will not announce "hourly forecasts".
Tapping on anything on the bottom table, will announce "Daily forecasts" before continuing to describe the element you tapped on. Tapping anything else in this table will not prefix with "Daily Forecasts".
Is there a simple API to name sections of your app? Or do you have to do this manually by tracking the voiceover cursor and dynamically changing your label? (Does this even work? Can you change the accessibilityLabel after something is tapped but before it is read?)

There are two approaches I guess:
Subclassing the UITableViewCell and overriding the accessibilityLabel.
- (NSString *) accessibilityLabel
{
NSString* voiceOverString;
// append section title on voiceOverString and then the elements value
return voiceOverString;
}
See this link from Apple docs:
You can setAccessibilityLabel of the cell from cellForRowAtIndexPath. The example is for the weather app itself.

Is there a simple API to name sections of your app?
It seems like the most appropriate reference is Apple's Accessibility Programming Guide.
And its API, Apple's UIAccessibility Documentation.
Setting the shouldGroupAccessibilityChildren property seems like the best way to accomplish your goal. The linked API describes it as,
A Boolean value indicating whether VoiceOver should group together the elements that are children of the receiver, regardless of their positions on the screen. Setting the value of this property to YES on the parent view of the items in the vertical columns causes VoiceOver to respect the app’s grouping and navigate them correctly.
Things to keep in mind:
Is the target element an accessibility element? (you can check using the isAccessibilityElement property; standard UIKit controls and views implement the UIAccessibility protocol by default)
If so, you just need to set its accessibility attributes
If not, you need to change its value, view.isAccessibilityElement = true
The accessibilityLabel property identifies the element
The accessibilityHint property describes the action triggered by an element
You can set accessibility attributes in the storyboard
Or, you can set accessibility attributes in the implementation of your view subclass

Related

Accessibility on UIStepper

I know that voiceover can a be bag of hurt when used with an UIStepper because the UIButtons that it contains can't be customised. However I use this control to change a value displayed on a label:
I don't want to insert a new control just for voiceover and subclassing a control like UIStepper doesn't seem a good solution. Any ideas to implement voiceover with this interface?
You could have a view which wraps your label(s) and stepper together and which deals with the accessibility. So, the subviews are all disabled for accessibility and the wrapper view presents the text in the labels, the current value of the stepper and provides / handles a swipe based interface to increment and decrement the stepper. So, overall, the wrapper view would work like a slider.
An interface implementing a stepper may be well vocalized and presented by VoiceOver using the adjustable values.
A complete explanation is detailed here including illustrations and code snippets (ObjC + swift).

Apple Watch v1.01 Voice Over Accessibility of WKInterfaceTable rows doesn't work

I'm trying to get some simple Voice Over setup working on my Watch app. I use a WKInterfaceTable who's rows have multiple elements within them. At the moment voice over just goes over each individual element reading them out.
I want to set that each of my rows
isAccessibilityElement
to YES so that it will itself become voice over selectable and will hide its children elements.
Unfortunately this just doesn't seem to work. The rowcontroller's have to inherit off of NSObject, not WKInterfaceObject and it just seems to not be respecting the isAccessibilityElement property. I set it, and the label for each rowController but voice over continues to just select the children of the row, and ignore any of the accessibility setup I've got on the rowController itself.
I've seen a WWDC video this year talking about this stuff, I'm presuming this is an WatchKit 2.0 feature that this stuff all works, or has anyone had any success getting their Watchkit 1.0 apps to have anything other than the most simple built in voice over accessibility?
Thanks for your time
Instead of setting the accessibilityHint and accessibilityLabel on the row itself, connect the main Group to your Cell NSObject class as well and set the accessibility properties on this group:
#IBOutlet var mainGroup: WKInterfaceGroup!
and
let row = workoutTypeSelectionTable.rowController(at: index) as! WorkoutSelectionCell
row.mainGroup.setAccessibilityHint("Double tap to start.")
row.mainGroup.setAccessibilityLabel("Start a new \(disciplineTitle) session.")

iOS Accessibility - is there a way to tell when VoiceOver has changed focus?

I'd like to call a method every time a different element is focused while VoiceOver is active. I was hoping there would be some UIAccessibilityNotification for this, but I can't seem to find any.
Ultimately, my goal is to add an additional condition prior to reading the accessibility label. For example, as opposed to saying (by default) "If UIButton becomes focused: read label", I'd like to be able to say "When UIButton becomes focused AND UIButton's background color is blue: read label".
So my question is: how do I either add an additional condition prior to reading the label, or receive a notification when a new element becomes focused?
You can't explicitly tell when the user moves the VoiceOver cursor (just like you can't tell where a sighted user is looking).
For the behavior you want, you have two options:
Set the button's accessibilityLabel to an appropriate value whenever the other conditions change.
Subclass UIButton and override its accessibilityLabel getter method:
- (NSString *) accessibilityLabel {
if (SOME_CONDITION) {
return #"Hooray!";
} else {
return #"Womp womp";
}
}
If you need to disable an item entirely, rather than returning nil or a blank string, you should set its accessibilityElementsHidden property to YES.
You can use the UIAccessibilityFocus protocol to detect changes in focus by accessibility clients (including VoiceOver). Note that UIAccessibilityFocus is an informal protocol that each accessibility element must implement independently.
That said, for your use case, Aaron is right to suggest returning a different accessibilityLabel under each condition.

Swipe to switch focused accessibility element iOS

When the user has Voice Over on in certain apps, a one handed swipe to the right or the left changes the focused accessibility element and speaks it (for example, the App Store top charts view). I would like to have this in my own app (which uses a storyboard).
I can think of several ways to do this myself with a swipe gesture recognizer and a list of accessibility elements in order, but it seems like there must be a way to do this in the accessibility API. However, my research has turned up nothing.
Is this a built in feature? If so, how can I add it in my storyboard or in code?
Edit:
Per advice from one of the answer I have implemented the UIAccessibility protocol for my view.Here is the code.
- (NSInteger)accessibilityElementCount{
return 4;
}
- (id)accessibilityElementAtIndex:(NSInteger)index{
return [#[self.menuButton, self.firstButton, self.secondButton, self.thirdButton] objectAtIndex:index];
}
- (NSInteger)indexOfAccessibilityElement:(id)element{
return [#[self.menuButton, self.firstButton, self.secondButton, self.thirdButton] indexOfObject:element];
}
The view I am having this issue with is defined in an interface builder storyboard. As you can no doubt infer from the code, it has 3 buttons as subviews.
What you are describing is the built-in behavior for VoiceOver and can't be changed on a per-app basis.
If you want to modify the order elements are focused, look at the UIAccessibilityContainer protocol for iOS 7 or accessibilityElements property of NSObject for iOS 8. If you don't want to implement either of those, you can also simply set accessibilityElementsHidden to YES for elements you want VoiceOver to ignore.
I have fixed the problem by adding accessibility labels to the buttons in the storyboard. Because voice over already spoke their label correctly, I had not bothered to do so before.

How can I add to the iOS VoiceOver rotor for a custom view?

Recently, I've been working to get my application functioning well with VoiceOver. Generally it's been simple and straightforward, but there are some behaviors from system apps that I'd like to emulate, and I'm having a hard time locating the API to set things up.
In particular, I'm interested in adding a couple of options to the VoiceOver "rotor" and responding to them when the user increases and decreases the value. However, despite the fact that apps like Apple's Maps app add items to the rotor and are able to respond, I can't figure out how to do so for my app.
Has anyone succeeded in doing this? And if so, how?
With iOS 8, you can use the -accessibilityCustomActions method to return an array of UIAccessibilityCustomAction objects, representing the actions you'd like to present "rotor-style".
UPDATE: iOS 10 finally adds ability to add custom rotor items to VoiceOver (not the same thing as the "Actions" rotor item) - just add array of UIAccessibilityCustomRotor objects to accessibilityCustomRotors of the appropriate container view.
OLD ANSWER:
There is currently no API to add your own rotor items. You can only implement how some of the existing rotor items work:
"Adjust value" - here you should return UIAccessibilityTraitAdjustable trait for accessibilityTraits and then implement the accessibilityIncrement/accessibilityDecrement methods
"Headings" - you mark some views as UIAccessibilityTraitHeader, then those should be the view the user moves through when the user rotates to "Headings" and flicks up/down
OLD UPDATE: "Actions" - see UIAccessibilityCustomAction
I guess you should file a radar if you need to add custom items to rotor.

Resources