Search code examples
uitableviewios6ios-ui-automationuiaccessibility

Set accessibilityIdentifier on custom table section header view for UIAutomation


How does one set an accessibility identifier on a custom section header view in a UITableView?

For background, since logging the element tree of a table view inside UIAutomation produces a flat list of table cells (UIATableCell instances) intermingled with table group elements (UIATableGroup), being able to identify groups makes it easier to identify the cells that belong to those groups (since they are returned in order).

If I set an accessibilityIdentifier explicitly on a custom view that gets returned as a section header view, I can confirm that the accessibilityIdentifier property is indeed set on that view.

Here's that method that feeds the custom section header view (which does show up in my actual table view, of course):

 - (UIView *)tableView:(UITableView *)tableView 
viewForHeaderInSection:(NSInteger)sectionNumber
{
    UIView *headerView = [self someMethodToRetrieveHeaderView];

    // This line is logging that indeed, the accessibility identifier is set.
    NSLog(@"Header view accessibility identifier is: '%@' for section number: %d",
     headerView.accessibilityIdentifier, sectionNumber);         
    return headerView;
}

The problem becomes that when I issue a logElementTree() call in JavaScript in my UIAutomation test against this table view, it gives me back a UIATableGroup element that has a name derived from the textual content inside that section header view (i.e. falling back to UIAccessibilityLabel heuristics). Because that section header has a segmented control in it, I get inconsistent accessibilityLabel values. Hence, my desire to bypass all of that and assign an explicit identifier.

How do I force my own explicit accessibilityIdentifier to show up as the UIATableGroup's name property instead?


Solution

  • You have to make sure that the view you're returning also responds to isAccessibilityElement with YES. I was able to solve this by experimenting with the core data books sample app that Apple provides.

    I implemented a custom view like so:

    static int counter = 0;
    
    @interface MyView : UIView
    @end
    
    @implementation MyView
    
    - (NSString *)accessibilityIdentifier
    {
        return [NSString stringWithFormat:@"Custom Identifier %d", counter++];
    }
    
    - (BOOL)isAccessibilityElement
    {
        return YES;
    }
    
    @end
    

    And then I returned it to the table view delegate (which is the table view controller in this case):

    - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
    {
        MyView *v = [[MyView alloc] init];
        return v;
    }
    

    I think what's happening is that the accessibility infrastructure is looking at the header view and trying to grab the "identifier" of the first subview that says that it is indeed an accessibility element. So, in your case, the segmented control is returning YES to isAccessibilityElement so that is the trigger to the accessibility APIs that this identifier is the one that should be exposed.

    So, the solution is to make sure that the UIView you are returning as the header returns YES to that method in addition to returning a custom identifier.