Checkpoint 502.3.3 Row, Column, and Headers

If an object is in a data table, the occupied rows and columns, and any headers associated with those rows or columns, shall be programmatically determinable.

Rationale

Table organization and relationship information displayed visually must also be available programmatically so that the information is accessible to everyone. Sighted users take many cues from the table and can easily see whether there are row or column headers and can read the data in a single row or column and associate it with the correct header information. Screen readers must have this information programmatically identified so that the intended relationships and organization shown in the presentation of the table can be conveyed to the user who cannot see it. As an example, a table header must be coded as a header; it is not sufficient to simply place the header information in the first row or first column and give it a special visual treatment such as bold text or a colored background.

Development Techniques

Note: Review the General techniques as well as other tabs applicable to your technology.  Prioritize the use of technology-specific techniques, and implement the General techniques as needed. You are always required to find, understand and implement accessible code techniques to meet the checkpoint. The documented techniques and supplements are not exhaustive; they illustrate acceptable ways to achieve the spirit of the checkpoint. If numbered, techniques are in order of preference, with recommended techniques listed first.

General techniques

Each item in this section represents a technique or combination of techniques deemed sufficient.

  • Programmatically identifying tabular controls as tables
  • Programmatically exposing row and column headers for a table control
  • Programmatically exposing caption and summary for a table control

Mobile Native (iOS) techniques

Instructions: In addition to the General techniques, any item in this section represents a technique deemed sufficient where appropriate.

Note: The tecniques shown here also appear in Checkpoint 1.3.1 - Info and Relationships.

Handling iOS UITableView contents

iOS UITableView implements a table as a single column with sometimes multiple pieces of data within the cell. (Multi column tables or grids are shown in a later example.) Contents on UITableView cells are typically summary information, images, or icons which can be selected to open a detail page. The contents may read without further coding if they have accessible content or text. The developer is responsible for providing any needed modifications to what should be spoken and for setting the accessibilityLabel of the UITableViewCell. If the cells in UITableView can open a detail page or perform another action, they must be labeled buttons so it is clear they can be pressed.

The following example 1 code sets a UITableViewCell label and button UIAccessibilityTrait on the cell in a UITableVIew:

- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
  {
    // Get prior saved cell or create one
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"UITableViewCell"];
    if (!cell){
        cell = [[UITableViewCell alloc]
        initWithStyle:UITableViewCellStyleDefault       reuseIdentifier:@"UITableViewCell"];
      }

    [cell.textLabel setText:@"Visible Text"];
    cell.accessibilityLabel = @"Accessible Text";
    cell.accessibilityTraits = UIAccessibilityTraitButton;

    return cell;
  }

 Figure 1: Example 1 code sample

Providing semantic information in a grid (table)

iOS does not have a native UI element for a data grid similar to an HTML table (referred to here as a grid to distinguish from a UITableView).

One alternative is to make the mobile application a hybrid by creating an HTML table in an embedded UIWebView. HTML tables in a UIWebView provide built-in handling for VoiceOver. Refer to example 3 in the mobile iOS native techniques section of Checkpoint 1.3.1 - Info and Relationships for sample code to embed a UIWebView.

Another alternative is to display content  through native APIs in a custom grid control. It must provide navigation between cells and additional column and row information to provide context for the user. The end result, at a minimum, is that when the user navigates the table data, he must hear the cell data and the column header if the new cell has a different row and / or column header than the previously heard cell. Until iOS provides a native UI element, a set of APIs for a datagrid, or some easy way to track previous cells, it is acceptable to provide the data, column header, and row identifier for each cell.

In the following example 2 code fragment, an array of UIAccessibilityElements is created to overlay a data grid. VoiceOver navigates through the UIAccessibilityElements and provides the mapped accessibility information.

/* Return the array of UIAccessibilityElements, or create if does not exist. */
- (NSArray *)accessibleElements
  {
    if ( accessibilityElements != nil )
    {
      return accessibilityElements;
    }
    accessibilityElements = [[NSMutableArray alloc] init];
    for (int iRow = 0; iRow < weatherTable.getRows; iRow++){
      for (int iCol = 0; iCol < weatherTable.getColumns; iCol++){
        NSMutableAttributedString *accessibleString = [[NSMutableAttributedString alloc] init];
        NSAttributedString *cellString = [[NSAttributedString alloc] initWithString:[weatherTable getCell:iRow :iCol] ];
        [accessibleString appendAttributedString:cellString];
        // Add the header information if not a header column/row
        if (iRow>0 & iCol>0){
          NSAttributedString *rowHeaderString = [[NSAttributedString alloc]     initWithString:[weatherTable getCell:0 :iCol] ];
          [accessibleString appendAttributedString:rowHeaderString];
          NSAttributedString *columnHeaderString = [[NSAttributedString alloc] initWithString:[weatherTable getCell:iRow :0] ];
          [accessibleString appendAttributedString:columnHeaderString];
        }
      element1.accessibilityFrame = CGRectMake(20+iCol*100,100+iRow*50,100,50);
      element1.accessibilityLabel = [accessibleString string];
      [accessibilityElements addObject:element1];
    }
  }
  return accessibilityElements;
}
/* Draw the visible grid of data. */
- (void) drawRect:(CGRect)rect
  {
    UIFont* font = [UIFont fontWithName:@"Arial" size:14];
    UIColor* textColor = [UIColor blackColor];
    NSDictionary* stringAttrs = @{ UITextAttributeFont : font,     UITextAttributeTextColor : textColor };
    for (int iRow = 0; iRow < weatherTable.getRows; iRow++){
      for (int iCol = 0; iCol < weatherTable.getColumns; iCol++){
        NSAttributedString* attrStr = [[NSAttributedString alloc]    initWithString:[weatherTable getCell:iRow :iCol] attributes:stringAttrs];
        // The visible grid matches the accessible grid
        [attrStr drawInRect:CGRectMake(20+iCol*100,20+iRow*50,100,50)];
      }
    }
  }
/* The container itself is not accessible, so the container view should return NO in isAccessiblityElement. */
- (BOOL)isAccessibilityElement
  {
    return NO;
   }
/* The following methods are implementations of UIAccessibilityContainer protocol methods. */
- (NSInteger) accessibilityElementCount {
    return [[self accessibleElements] count];
  }
- (id)accessibilityElementAtIndex:(NSInteger)index
  {
    UIAccessibilityElement *element = [[self accessibleElements]     objectAtIndex:index];
    return element;
  }
- (NSInteger)indexOfAccessibilityElement:(id)element
  {
    int iSelectedIndex = [[self accessibleElements] indexOfObject:element];
     return iSelectedIndex;
   }

 Figure 2: Example 2 code sample

Create a table in iOS

The only way to create a multi-row and column table in native iOS is to create a custom control by using a combination of other, already existing controls or by writing one from scratch. For example, a custom table can be implemented using labels, or text fields.

In any case, all accessibility information has to be exposed. For VoiceOver users, this may not be sufficient. We recommend tracking the users' navigation, and exposing information to VoiceOver to ensure the application provides the relevant information for any given table cell.

An example custom table can be found in Apple's Numbers app. When headers are turned on in VoiceOver, and the user navigates to the next data cell, row and column headers are announced when the cell has a different row or column header than the previous cell.

Eclipse techniques

Instructions: In addition to the General techniques, any item in this section represents a technique deemed sufficient where appropriate.

Note: The purpose of this checkpoint is to ensure that contextual relationship information is provided to assistive technologies about the table control and its children including table cells, row and column headers.

Programmatically identifying tabular controls as tables

For tabular controls created using the org.eclipse.swt.widget.Control, such as tables, grids and calendars, the accessible role must be set to ROLE_TABLE by adding an AccessibleConrolListener to the control as outlined in checkpoint 502.3.1 Object Information.

For the child elements that make up the table, add an AccessibleControlListener to each column header, row header and table cell exposing their corresponding roles of ROLE_TABLECOLUMNHEADER, ROLE_TABLEROWHEADER and ROLE_TABLECELL.

Implement other methods of AccessibleListener and AccessibleControlListener as appropriate to expose name, description, state and information about the child elements for the table and its children.

Programmatically exposing row and column headers

Add an AccessibleTableListener to the table's Accessible and add an AccessibleTableCellListener to the Accessible of each table item including the cells, column headers and row headers. This will allow an assistive technology to query the cell's contextual information including the table cell header details. Implement the methods of the AccessibleTableListener and AccessibleTableCellListener as appropriate for your table control implementation.

To expose the relationship between a table cell and its headers, implement the getColumnHeaders and getRowHeaders methods of the AccessibleTableCellListener. These methods are more efficient than getColumnHeaderCells and getRowHeaderCells of the AccessibleTableListener.

The following code snippet taken from the SWT AccessibleTable example demonstrates how to return the accessible of the column header associated with the table cell:

accessible.addAccessibleTableCellListener(new AccessibleTableCellListener() {
              public void getColumnHeaders(AccessibleTableCellEvent e) {
                   if (parent.columns.length == 0) {
                        /* The CTable is being used as a list, and there are no headers. */
                        e.accessibles = null;
                   } else {
                        /* CTable cells only occupy one column. */
                        CTableColumn column = parent.columns [columnIndex];
                        e.accessibles = new Accessible[] {column.getAccessible (accessibleTable)};
                   }
              }

Programmatically exposing caption and summary for table control

If a caption and/or summary exist, each must be programmatically made available to assistive technologies (AT).

  • The caption identifies the table and provides its accessible name. Add a RELATION_LABELLED_BY between the table and its caption and a RELATION_LABEL_FOR conversely between the caption and the table.
  • The summary provides further description about how the data in the table is organized or how to navigate the table. Add a RELATION_DESCRIBED_BY between the table and the summary and conversely a RELATION_DESCRIPTION_FOR between the summary and the table. The summary should also be returned as the accessible description for the table.
  • If both a summary and caption are defined for the table, they should not duplicate each other.

The following code snippet provides further details:

final Label caption = new Label(group, SWT.NONE);
caption.setText("CTable with column headers");
        
final Label summary = new Label(group, SWT.NONE);
summary.setText("This example uses a customized table.");
summary.setVisible(false);
        
caption.getAccessible().addRelation(ACC.RELATION_LABEL_FOR, table1.getAccessible());
table1.getAccessible().addRelation(ACC.RELATION_LABELLED_BY, caption.getAccessible());
summary.getAccessible().addRelation(ACC.RELATION_LABEL_FOR, table1.getAccessible());
table1.getAccessible().addRelation(ACC.RELATION_LABELLED_BY, summary.getAccessible());
    
table1.getAccessible().addAccessibleListener(new AccessibleAdapter() {
    @Override
    public void getName(AccessibleEvent event) {
        event.result = caption.getText();
    }
    @Override
    public void getDescription(AccessibleEvent event) {
        event.result = summary.getText();
    }
});

Programmatically notifying assistive technologies of changes to the table and its child elements

In addition to notifying assistive technologies of focus and selection change events for the table and table cells, programmatically notify assistive technologies of events involving the table and its child elements as defined in org.eclipse.swt.accessibility.ACC including changes to caption, summary, column and row headers and column and row descriptions.

For more information on providing event notifications to assistive technologies, refer to checkpoint 502.3.14 Event Notification.

Windows-based (MSAA+IA2) techniques

Note: The purpose of this checkpoint is to ensure that contextual relationship information is provided to assistive technologies about the table control and its children including table cells, row and column headers.

Instructions: In addition to the General techniques, refer to the Windows techniques tab in checkpoint 502.3.1 Object Information to learn about accessibility APIs, and use the following to support Windows accessibilty.

Implementing appropriate interfaces for the table control and its child elements

In addition to implementing IAccessible, the IAccessible2 and IServiceProvider interfaces must be implemented on the table and its child elements. To provide proper access to assistive technologies to the table control and its children implement IAccessibleTable2 for the table control and IAccessibleTableCell for each cell in the table. Refer to the IAccessible2 Implementation Guide for full details on providing IAccessible, IAccessible2 and IServiceProvider interfaces.

Programmatically identifying tabular controls as tables

For tabular controls such as tables, grids and calendars, the accessible role must be set to ROLE_SYSTEM_TABLE. For the child elements that make up the table expose their corresponding roles of ROLE_SYSTEM_COLUMNHEADER, ROLE_SYSTEM_ROW, ROLE_SYSTEM_ROWHEADER, ROLE_SYSTEM_COLUMN and ROLE_SYSTEM_COLUMNHEADER. For convenience, these MSAA roles, should be passed through the IAccessible2::role method as well as the IAccessible object's get_role method.

Programmatically exposing relations between table, cells, rows, columns and headers

Implement the methods of IAccessibleTable2 to provide information to assistive technologies about the table's rows and columns including their count, description and selection, information about selected cells and the ability to retrieve a cell at a given row and column index.

For table controls that dynamically change in size, the IAccessibleTable2::modelChange function should be implemented to indicate what has changed.

Implement IAccessibleTableCell methods for all table cells to programmatically expose information about the cell's index, extent, selection and its relation to row and column header cells.

Programmatically exposing caption and summary for table control

While they are not required for tables, captions and summaries are fairly straightforward ways to provide information that can help users find, navigate, and understand tables.

Notes:

Programmatically notifying assistive technologies of changes to the table and its child elements

In addition to notifying assistive technologies of focus and selection change events for the table and table cells, programmatically notify assistive technologies of events involving the table and its child elements as defined in the IA2EventID enum prefixed with IA2_EVENT_TABLE.

For more information on providing event notifications to assistive technologies, refer to checkpoint 502.3.14 Event Notification.


Most links in this checklist reside outside ibm.com at the Web Content Accessibility Guidelines (WCAG) 2.0. W3C Recommendation 11 December 2008: http://www.w3.org/TR/WCAG20/

Copyright © 1994-2017 World Wide Web Consortium, (Massachusetts Institute of Technology, European Research Consortium for Informatics and Mathematics, Keio University, Beihang University). All Rights Reserved.

Copyright © 2001, 2017 IBM Corporation