I've created a custom accessibility rotor to allow navigating through custom annotation views following the example djibouti33 provides here: Create a custom VoiceOver Rotor to navigate MKAnnotationViews. It works after selecting the custom rotor, but the rotor for the MKMapView always defaults to some other selection. The ability to navigate through the custom annotations makes the most sense in the context of the App (i.e. place priority on navigating through the App-specific annotations). Is there a way to have the custom rotor selected by default?
Related to this, I think it would also be more intuitive to have VoiceOver state the option to select the custom rotor, but when the MKMapView is touched is VoiceOver always states "Use the rotor to select points of interest". Changing the map view accessibilityHint like this had no effect:
mapView.accessibilityHint = "use the rotor to access alerts"
Is it possible to change what VoiceOver speaks when a MKMapView is selected?
When a custom rotor is created, it's added to the bunch of rotor actions selected by the user in his settings.
Unfortunately, user settings is kind of private box you cannot access in this case, that's why a custom rotor cannot be a default selection, it's a willingly user choice.
About the second question, your mapview is a container and, as a parent view that wants to show its children as accessible elements, its isAccessibilityElement property is false meaning that label or hint won't be analyzed by VoiceOver.
If you want to reach the accessible elements inside your map, your MKMapView itself will be never selected (only its elements) and then VoiceOver won't read out anything for this particular case because it's an invisible element for it.
According to your application presentation, you may post a notification while loading the view or add an accessible element before your map indicating that the rotor may be used to reach some information for instance.
Related
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
In the setting app if I double tap on the 'General' row. It pushes the the general view controller. It then says
"General"
(The name of the current view controller)
Then
"Settings, back button"
(The name of the selected item)
However in my app with a custom self.navigaitonItem.titleView it only says
"Home, Back Button"
How do I get it to read out the name of the screen? (I tired to set self.title)
I havent tested it but you might need to check out the accessibilityLabel property of the view.
From documentation:
By default, standard UIKit controls and views have labels that derive from their titles. If you provide a custom control or view, however, you need to set this property appropriately so that assistive applications can supply accurate information to users with disabilities.
When using ECSlidingViewController2, How can we prevent the under left menu from being tapped with VoiceOver on and the menu is under the top view? Try the BasicMenu example. When on the Home view, you can tap the options on the menu underneath.
If you dont want the voice over to read it, then make it inaccessible. Set the isAccessibleElement to NO.
This is a master switch for whether the element is accessible or not. UIViews and any custom direct subclasses of it are not accessible by default, whereas UIControls are. Elements which are not marked as accessible will be ignored by VoiceOver, and will be skipped when the user is navigating between accessible elements.
- (BOOL)isAccessibilityElement {
//if this is YES, VoiceOver won't continue to look for accessibility elements in this view's subviews
return NO;
}
Also you can set this to any UIView in your app.
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.
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.