Change order of read items with VoiceOver
The easiest answer to this lies in creating a UIView
subclass that contains your buttons, and responds differently to the accessibility calls from the system. These important calls are:
-(NSInteger)accessibilityElementCount
-(id)accessibilityElementAtIndex:
-(NSInteger)indexOfAccessibilityElement:
I've seen a few of these questions, and answered one before, but I've not seen a generic example of how to reorder the VoiceOver focus. So here is an example of how to create a UIView
subclass that exposes its accessible subviews to VoiceOver by tag.
AccessibilitySubviewsOrderedByTag.h
#import <UIKit/UIKit.h>
@interface AccessibilitySubviewsOrderedByTag : UIView
@end
AccessibilitySubviewsOrderedByTag.m
#import "AccessibilityDirectional.h"
@implementation AccessibilitySubviewsOrderedByTag {
NSMutableArray *_accessibilityElements;
}
//Lazy loading accessor, avoids instantiating in initWithCoder, initWithFrame, or init.
-(NSMutableArray *)accessibilityElements{
if (!_accessibilityElements){
_accessibilityElements = [[NSMutableArray alloc] init];
}
return _accessibilityElements;
}
// Required accessibility methods...
-(BOOL)isAccessibilityElement{
return NO;
}
-(NSInteger)accessibilityElementCount{
return [self accessibilityElements].count;
}
-(id)accessibilityElementAtIndex:(NSInteger)index{
return [[self accessibilityElements] objectAtIndex:index];
}
-(NSInteger)indexOfAccessibilityElement:(id)element{
return [[self accessibilityElements] indexOfObject:element];
}
// Handle added and removed subviews...
-(void)didAddSubview:(UIView *)subview{
[super didAddSubview:subview];
if ([subview isAccessibilityElement]){
// if the new subview is an accessibility element add it to the array and then sort the array.
NSMutableArray *accessibilityElements = [self accessibilityElements];
[accessibilityElements addObject:subview];
[accessibilityElements sortUsingComparator:^NSComparisonResult(id obj1, id obj2){
// Here we'll sort using the tag, but really any sort is possible.
NSInteger one = [(UIView *)obj1 tag];
NSInteger two = [(UIView *)obj2 tag];
if (one < two) return NSOrderedAscending;
if (one > two) return NSOrderedDescending;
return NSOrderedSame;
}];
}
}
-(void)willRemoveSubview:(UIView *)subview{
[super willRemoveSubview:subview];
// Clean up the array. No check since removeObject: is a safe call.
[[self accessibilityElements] removeObject:subview];
}
@end
Now simply enclose your buttons in an instance of this view, and set the tag property on your buttons to be essentially the focus order.
Accessibility for iOS, VoiceOver read order issue
The quickest way to achieve this for your example is to place the three labels in a transparent UIView
subclass to serve as a container for your labels. This subclass will have to be properly setup to let VoiceOver know how to interpret it. If your deployment target is iOS6
then you can simply answer the "should group accessibility children" question in this subclass.
-(BOOL)shouldGroupAccessibilityChildren{
return YES;
}
For below iOS6
it would be more complicated, except that your UIView
container subclass would contain only UILabels
which are accessibility elements. You could implement it like this:
-(BOOL)isAccessibilityElement{
return NO;
}
-(NSInteger)accessibilityElementCount{
return self.subviews.count;
}
-(id)accessibilityElementAtIndex:(NSInteger)index{
return [self.subviews objectAtIndex:index];
}
-(NSInteger)indexOfAccessibilityElement:(id)element{
return [self.subviews indexOfObject:element];
}
I have tested this example code and it does what you are looking for, if you need any clarification please add a comment. Always happy to help make things more accessible.
How can I change the order of accessibility elements in a view controller without losing access to the navigation bar?
I tried and reproduce the problem you mentioned in a blank project following this storyboard :
I read this a11y recommendations site to provide this code snippet I implemented to make it work as desired :
class TestButtonTableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
@IBOutlet weak var myTableView: UITableView!
@IBOutlet weak var bottomButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
myTableView.delegate = self as UITableViewDelegate
myTableView.dataSource = self as UITableViewDataSource
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.accessibilityElements = [bottomButton, myTableView]
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
return 2
}
func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
return zeCell = tableView.dequeueReusableCell(withIdentifier: "myPersoCell",
for: indexPath)
}
}
I made right flicks to get the next elements and obtained the illustrations hereunder :
The VoiceOver navigation follows the desired pattern :
- Back button (navigation bar).
- Title (navigation bar).
- Edit button (navigation bar).
- Floating button.
- Table contents.
I specified nothing in particular and changed the order of accessibility elements in a view controller without losing access to the navigation bar.
Related Topics
How to Dismiss Keyboard When Touching Anywhere Outside Uitextfield (In Swift)
Why Does Uiviewcontroller Extend Under Uinavigationbar, While Uitableviewcontroller Doesn'T
iOS Avplayer Trigger Streaming Is Out of Buffer
Facebook Share Content Only Shares Url in iOS 9
Ios: Change the Height of Uisegmentedcontrol
Uitableview Add Cell Animation
Warp \ Bend Effect on a Uiview
Avplayer Stalling on Large Video Files Using Resource Loader Delegate
Sectionindextitles for a Uicollectionview
Bringing a Subview to Be in Front of All Other Views
iOS How to Asynchronously Download and Cache Images and Videos for Use in My App
Cocoapods: Unable to Find a Specification for [Privatespec] Depended Upon by [Privateclientspec]
How to Turn Flashlight on and Off in Swift
No Round Rect Button in Xcode 5
Detect If a Point Is Inside a Mkpolygon Overlay
Populating Tableview with Multiple Sections and Multiple Dictionary in an Array in Swift