Change Order of Read Items with Voiceover

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 :
Sample Image
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 :
Sample Image
Sample Image
The VoiceOver navigation follows the desired pattern :

  1. Back button (navigation bar).
  2. Title (navigation bar).
  3. Edit button (navigation bar).
  4. Floating button.
  5. 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



Leave a reply



Submit