Apple Developer Connection
Advanced Search
Member Login Log In | Not a Member? Support

Adding Accessibility Support in Custom Views in Your Cocoa Application

This article is the second in a two-part series on adding accessibility to your Cocoa application. In the first article of this series, Enabling Accessibility in Your Cocoa Application, we led off with a general overview of the accessibility model in Mac OS X. It introduced the NSAccessibility informal protocol, accessibility objects and their attributes, and looked at how the accessibility hierarchy differs from the Cocoa containment hierarchy. The first article also covered the basics of access enabling a Cocoa application.

In this second article, we show you an implementation of the NSAccessibility protocol for a custom subclass of NSView.

Access Enabling A Custom Control From Scratch

In Part I we looked at some of the general concepts of the Mac OS X accessibility model from a Cocoa perspective, then finished up with some basic alterations needed to access enable a Cocoa application. We will continue working with the same example application from Part I, ImageMapExample, which you can download from the ADC Reference Library.

Before we go on, let's review what we've done with the ImageMapExample application:

  • Created a conceptual link between two controls using the TitleUIElement accessibility attribute.
    We saw how to do this the quick and easy way in Interface Builder, and programmatically.
  • Set description attributes of a segmented control programmatically.
    The SetSegmentDescriptions() function demonstrated finding your way through the accessibility hierarchy to make sure you are setting accessibility attributes properly. You could use this function in your own projects.

It is important to realize that what we have covered so far will go a long way in the vast majority of cases. The rest of this article covers a more involved case of access enabling a non-trivial custom control from scratch.

Subclassing an Access Enabled Class

The food pyramid control in ImageMapExample is a subclass of NSView. It displays an image (FoodPyramid.gif), and then divides the image up into hotspots than the user can click to perform some action. The ImageMap class is similar to an HTML client-side image map. In fact, ImageMap does use an HTML file to specify the hotspots (FoodPyramid.html). The ImageMap gives some control over how the hotspots are highlighted. These options are set by the segmented control in the main window.

The boundaries of each hotspot are represented by NSBezierPath objects. The ImageMap class has an NSMutableArray instance variable to keep track of the NSBezierPath objects for each hotspot. The NSBezierPaths are used by the ImageMap class to handle hit-testing (including accessibility hit-testing).

Because every subclass of NSView (as the ImageMap class is) inherits an implementation of the NSAccessibility protocol, all you have to do is specialize the implementation for your subclass. Of the ten methods in the NSAccessibility protocol, we will only be overriding three in order to report our role correctly, as well as handle the hot spots in the image map as children.

Including the Accessibility Object in the Hierarchy

Going back to the discussion of the accessibility object hierarchy, recall that not every Cocoa view is relevant to an assistive application. In the NSAccessibility protocol, a method called accessibilityIsIgnored: returns a boolean value indicating whether or not the view is represented by an accessibility object in assistive applications. The default implementation of accessibilityIsIgnored: returns YES, so we must override this method to return NO in the custom view.

The default in other classes is not YES.

Understanding the Mac OS X accessibility hierarchy is an important concept, and you are strongly encouraged to read the first part of this series, Enabling Accessibility in Your Cocoa Application.

Open the file ImageMapAccessibility.m, and find the accessibilityIsIgnored: method:

- (BOOL)accessibilityIsIgnored {
    return NO;
}

Note: The ImageMap view class defines an Objective-C category called ImageMapAccessibility, to keep its implementation of the NSAccessibility protocol in one location. Though not required, this Objective-C design pattern can help you isolate and quickly locate protocol implementations.

Returning Values for the NSAccessibilityChildrenAttributeAccessibility Attributes

The NSAccessibility protocol defines a way for Cocoa user interface elements such as views to provide information to assistive applications about what attributes they support, and what the values are for those attributes. Returning attribute values is the purpose of the accessibilityAttributeValue: method. This method is passed in an NSString that is the name of the attribute for which you must return the value. The ImageMap class provides its own implementation of the accesibilityAttributeValue: method, located in the file ImageMapAccessibility.m.

- (id)accessibilityAttributeValue:(NSString *)attribute 
{
    if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) {
      return NSAccessibilityGroupRole;
    } else 
       if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute]) {
         return NSAccessibilityRoleDescriptionForUIElement(self);
       } else 
          if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
            int numHotSpots = [self numHotSpots];
            NSMutableArray *kids = [NSMutableArray arrayWithCapacity:numHotSpots];
            int i;
            for (i = 0; i < numHotSpots; ++i) {
               [kids addObject:[HotSpotUIElement elementWithIndex:i parent:self]];
            }
	
            // Handle the default as a giant hot spot - for accessibility purposes.
            if ([self hasDefault]) {
              [kids addObject:[HotSpotUIElement elementWithIndex:NSNotFound parent:self]];
            }
	
            return NSAccessibilityUnignoredChildren(kids);
          } else {
             return [super accessibilityAttributeValue:attribute];
    	    }
}

Here we see the ImageMap telling assistive applications the values of its accessibility object attributes. Note the required role and role description attributes, NSAccessibilityRoleAttribute, and NSAccessibilityRoleDescriptionAttribute. As mentioned the description of the class, the ImageMap is a group of hotspots, so we return a value of NSAccessibilityGroupRole. The helper function NSAccessibilityRoleDescription() is used to return the default role description string.

When asked for our child elements, we create an array of our virtual children—objects of class HotSpotUIElement (this will be explained in detail later on). For all other attributes, the ImageMap class calls up to its super class (NSView) to provide the value.† Notice that we've only needed to change three attributes. The remaining attributes, including the view's size, position, parent, and window are all provided by NSView's implementation

Enabling Hit-Testing

The accessibilityHitTest: method is called to determine the interface element under the mouse cursor. By their nature, custom controls have their own way of making this determination, so you need to provide a custom implementation of this method. For the ImageMap class, this amounts to figuring out which hotspot lies beneath the cursor.

When the accessibilityHitTest: method is called, the fact that your element contains the mouse cursor has already been established. For elements that contain children (as the ImageMap class does), the method allows you to return more specific information about the element that contains the cursor.

- (id)accessibilityHitTest:(NSPoint)point {
    NSPoint windowPoint = [[self window] convertScreenToBase:point];
    NSPoint localPoint = [self convertPoint:windowPoint fromView:nil];
    int index = [self hotSpotIndexForPoint:localPoint];
    if (index != NSNotFound || [self hasDefault]) {
	// Handle the default as a giant hot spot - for accessibility purposes.
	HotSpotUIElement *hotSpot = [HotSpotUIElement elementWithIndex:index parent:self];
	// Allow the hot spot to do further hit testing.
	return [hotSpot accessibilityHitTest:point];
    } else {
	return [super accessibilityHitTest:point];
    }
}

The NSPoint passed to the method is relative to the lower left corner of the screen, so first it must be converted to window coordinates, and finally to our view coordinates. Then, the ImageMap class uses its own methods to determine the hotspot containing the point. Recall that boundaries for each hotspot are recorded by the ImageMap class using an array of NSBezierPath objects. ImageMap searches its array, checking to see if the point is contained within any of its NSBezierPaths.

When the appropriate hotspot object is located, the ImageMap class calls the accessibilityHitTest: method on that specific hotspot. We will see exactly how the individual hotspots handle this message later on.

Setting the Description Attribute in Interface Builder

Now that we have overridden NSAccessibility methods so the ImageMap is no longer ignored, and its role and children are properly handled, we can do some fine tuning of what the image map reports.

We have already seen in the first article in the series how you can set some accessibility attributes for custom views using Interface Builder. Open the nib file again and select the ImageMap view on the main window. Open the Inspector window and choose Accessibility from the popup control. Notice the AXDescription field in the Inspector window. The description string for the ImageMap control is "food pyramid". Note that the string does not include any term that would describe its role, or class of the control. For example, the ImageMap control represents a group of clickable hotspots.

Assistive applications often combine the role description attribute with the description attribute, and then present the resulting string to the user. If we were to include the word "group" in the ImageMap's AXDescription, a screen reader such as VoiceOver would say, "food pyramid group group."

Why didn't we add a description attribute directly to the code?† In this application, the ImageMap shows a food pyramid, but the ImageMap class is designed to display any image. It doesn't make sense for the description of the ImageMap class to be hard coded.† Instead we set the description on this particular instance of the ImageMap class.

Access Enabling the Subelements of the ImageMap Class

Thus far we have made a subclass of NSView, and provided overrides for certain methods of NSAccessibility, such as accessibilityIsIgnored:, and accessibilityAttributeValue:. We must now provide a class so that the ImageMap can treat its hotspots as children.

When an assistive application communicates with the ImageMapExample application, one of the questions asked of the ImageMap control is, "give me a list of your child interface elements."

This list is an array of children objects which also implement the NSAccessibility protocol.†Since the implementation of the ImageMap class does not use subviews, cells, or any other object to represent hotspots, we will create a class whose sole purpose is to implement the NSAccessibility protocol, and provide the correct responses to accessibility requests.

Recall from the first part of the series, this is similar to the strategy employed by the NSScroller class.

Defining the FauxUIElement Class

In this example it is a hotspot that needs to be represented as a faux user interface element.† However, using faux user interface elements is a general enough accessibility technique that it would be useful to have a base class that you can reuse in your own projects.† The FauxUIElement class in this sample project is designed for this purpose.†

The FauxUIElement class implements the NSAccessibility protocol fully, allowing you to subclass and override just the parts of functionality that you need. The only instance variables a FauxUIElement needs are its role, and the object that is the FauxUIElement's parent. Don't be thrown off by the fact that the FauxUIElement class is not a descendant of any NSView-derived class. Any class that implements the ten methods of the NSAccessibility protocol can be used as a user interface element for accessibility purposes.

The ImageMapExample application specializes FauxUIElement, creating a subclass called HotSpotUIElement, which you saw previously in the ImageMap class' accessibilityAttributeValue: method. You can find the HotSpotUIElement class in the file ImageMapAccessibility.m.

Communicating with the Parent Class

Notice the FauxUIElement class doesn't contain any instance variables to record its size or position. Since it might represent a user interface element in any kind of custom control, the FauxUIElement class will defer to the parent control to handle these kinds of requests. This is the purpose of the informal protocol called FauxUIElementChildSupport, declared in FauxUIElement.h. The four methods of this protocol must be implemented by the parent class:

@interface NSObject (FauxUIElementChildSupport)

// Anyone serving as a parent for FauxUIElements must implement this protocol.

- (BOOL)isFauxUIElementFocusable:(FauxUIElement *)fauxElement;
- (void)fauxUIElement:(FauxUIElement *)fauxElement setFocus:(id)value;

- (NSPoint)fauxUIElementPosition:(FauxUIElement *)fauxElement;
- (NSSize)fauxUIElementSize:(FauxUIElement *)fauxElement;

Any class that serves as a parent to FauxUIElement objects must adopt the FauxUIElementChildSupport informal protocol. We will take a look at ImageMap's implementation shortly. First, let's look at the FauxUIElement class' implementation of the NSAccessibility protocol.

Implementing NSAccessibility

FauxUIElement's implementation of the ten NSAccessibility protocol methods is found in the file FauxUIElement.m. First, let's look at the methods dealing with attributes and their values.

The accessibilityAttributeNames: method is expected to tell assistive applications the names of all the attributes the accessibility object supports. In the FauxUIElement class, this method simply creates a singleton NSArray instance, initialized with NSString constants found in the file NSAccessibility.h.

- (NSArray *)accessibilityAttributeNames {
    static NSArray *attributes = nil;
    if (attributes == nil) {
	attributes = [[NSArray alloc] initWithObjects:
	    NSAccessibilityRoleAttribute,
	    NSAccessibilityRoleDescriptionAttribute,
	    NSAccessibilityFocusedAttribute,
	    NSAccessibilityParentAttribute,
	    NSAccessibilityWindowAttribute,
	    NSAccessibilityTopLevelUIElementAttribute,
	    NSAccessibilityPositionAttribute,
	    NSAccessibilitySizeAttribute,
	    nil];
    }
    return attributes;
}

The values of accessibility object attributes are returned by the method accessibilityAttributeValue:. The name of the desired attribute is passed in, and the code performs a number of comparisons against the string constants defined in NSAccessibility.h.

- (id)accessibilityAttributeValue:(NSString *)attribute {
    if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) {
	return role;
    } else if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute]) {
	return NSAccessibilityRoleDescription(role, nil);
    } else if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
	// Just check if the app thinks we're focused.
	id focusedElement = [NSApp accessibilityAttributeValue:NSAccessibilityFocusedUIElementAttribute];
	return [NSNumber numberWithBool:[focusedElement isEqual:self]];
    } else if ([attribute isEqualToString:NSAccessibilityParentAttribute]) {
	return NSAccessibilityUnignoredAncestor(parent);
    } else if ([attribute isEqualToString:NSAccessibilityWindowAttribute]) {
	// We're in the same window as our parent.
	return [parent accessibilityAttributeValue:NSAccessibilityWindowAttribute];
    } else if ([attribute isEqualToString:NSAccessibilityTopLevelUIElementAttribute]) {
	// We're in the same top level element as our parent.
	return [parent accessibilityAttributeValue:NSAccessibilityTopLevelUIElementAttribute];
    } else if ([attribute isEqualToString:NSAccessibilityPositionAttribute]) {
	return [NSValue valueWithPoint:[parent fauxUIElementPosition:self]];
    } else if ([attribute isEqualToString:NSAccessibilitySizeAttribute]) {
	return [NSValue valueWithSize:[parent fauxUIElementSize:self]];
    } else {
	return nil;
    }
}

In the code, you can see how the value of the FauxIUElement's role instance variable is returned. Again, the helper function NSAccessibilityRoleDescription() is used to get the default role description string.

One of a FauxUIElement's attributes is NSAccessibilityFocusedAttribute.† There is no need to get overly complicated when determining if your user interface element is the focused element. All you need to do is call up to the global application object. Using the NSAccessibility protocol method accessibilityAttributeValue: method, ask the application what it thinks the currently focused element is. If that element is you, return an NSNumber of the boolean YES, otherwise NO.

For the remaining attributes, you can see how the FauxUIElement forwards the requests to its parent. For the size and position, the FauxUIElementChildSupport informal protocol methods are called.

Assistive applications can alter the value of some accessibility object attributes. Your application—specifically, the user interface elements in your application—must tell the assistive application whether or they will allow this to happen. Two methods, accessibilityIsAttributeSettable:, and accessibilitySetValue:forAttribute:, exist for this purpose.

- (BOOL)accessibilityIsAttributeSettable:(NSString *)attribute {
    if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
	return [parent isFauxUIElementFocusable:self];
    } else {
	return NO;
    }
}

- (void)accessibilitySetValue:(id)value forAttribute:(NSString *)attribute {
    if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
	[parent fauxUIElement:self setFocus:value];
    }
}

The FauxUIElement implementation does not directly allow any attribute values to be set. However, through the FauxUIElementChildSupport protocol, FauxUIElement objects can ask their parent if it supports keyboard focus by calling the isFauxUIElementFocusable: method. The ImageMap class does not support keyboard focus for its child hotspot elements, so you will find it returning the value NO in its implementation of isFauxUIElementFocusable:.

To set the value of NSAccessibilityFocusedAttribute, the FauxUIElement class forwards the message on to its parent, although in the ImageMapExample application this method is never called because the ImageMap class returns NO when asked if the element is settable.

Because it is meant to be a lightweight, generic base class, FauxUIElement provides essentially empty implementations of the NSAccessibility methods dealing with actions. Specific implementations of these three methods are meant to be handled in a subclass, as we will see later with the HotSpotUIElement class.

- (NSArray *)accessibilityActionNames {
    return [NSArray array];
}

- (NSString *)accessibilityActionDescription:(NSString *)action {
    return nil;
}

- (void)accessibilityPerformAction:(NSString *)action {
}

Notice the accessibilityActionNames: method does not return nil, rather, it returns an empty array. Subclasses of FauxUIElement will override this method to provide an array containing the names of the actions they support.

Finally, we find implementations for the last three methods of NSAccessibility:

- (BOOL)accessibilityIsIgnored {
    return NO;
}

- (id)accessibilityHitTest:(NSPoint)point {
    return NSAccessibilityUnignoredAncestor(self);
}

- (id)accessibilityFocusedUIElement {
    return NSAccessibilityUnignoredAncestor(self);
}

FauxUIElement objects are not ignored, so an override of the accessibilityIsIgnored: method is provided to return the value NO.

The last two methods, accessibilityHitTest:, and accessibilityFocusedUIElement:, support mouse cursor hit-testing and keyboard focus, respectively. These methods are called when assistive applications want to determine the accessibility object for the current mouse cursor position, and for the element with keyboard focus. Recall from ImageMap's implementation of accessibilityHitTest:, when the accessibilityHitTest: method is called, the cursor position is already known to be within the bounds of the element receiving the call. Likewise, for keyboard focus, when the accessibilityFocusedUIElement: method is called, it has already been determined that the receiver has keyboard focus.

If the user interface element receiving these messages does not contain any children, all it has to do is agree with the assessment of the accessibility implementation, which it does by returning itself. If the element does have children, it would have to implement a way to return the exact element at the mouse cursor location, or with keyboard focus. That is exactly what the ImageMap class does in its own implementation of accessibilityHitTest:. Remember, once ImageMap figured out exactly which hotspot contains the cursor, it called accessibilityHitTest: on that object. Since a FauxUIElement does not have children, it can simply return itself.

If ImageMap supported keyboard focus for the hotspots, a similar chain of events would occur for the accessibilityFocusedUIElement: method.

It is important that you not return nil from these methods. Doing so means you disagree with the accessibility implementation. You are saying, "No, you are incorrect, the mouse is not over my element," or, "No, my element does not have keyboard focus." This is the incorrect thing to do.

Specializing FauxUIElement—The HotSpotUIElement Class

That's it for the FauxUIElement implementation of the NSAccessibility protocol, but we're not quite done yet. Although the FauxUIElement provides generic functionality, we'll create a subclass called HotSpotUIElement to hold the logic specific to hot spots.

The code for this class is found in the file ImageMapAccessibility.m.

The HotSpotUIElement class overrides the implementation of only a few NSAccessibility methods. Let's look at the specific implementations, starting with the accessibilityAttributeNames:, and the accessibilityAttributeValue: methods.

HotSpotUIElement objects must provide a description attribute. In the accessibilityAttributeNames: method, notice how the superclass implementation is called to create the initial array, then HotSpotUIElement adds an object for NSAccessibilityDescriptionAttribute.

- (NSArray *)accessibilityAttributeNames {
    ImageMap *imageMap = parent;
    if ([imageMap isHTMLImageMap]) {
	// For HTML image maps we can provide a description using the alt or title attributes.
	static NSArray *attributes = nil;
	if (attributes == nil) {
	    attributes = [[[super accessibilityAttributeNames] 
	    	arrayByAddingObject:NSAccessibilityDescriptionAttribute] retain];
	}
	return attributes;
    } else {
	return [super accessibilityAttributeNames];
    }
}

To return the value of the description, the HotSpotUIElement asks its parent (the ImageMap) for the HTML alt tag, initially provided in the FoodPyramid.html file. If this string is not present, the title string is returned. For all other attributes, the request for the value is passed along to the parent class, FauxUIElement.

- (id)accessibilityAttributeValue:(NSString *)attribute {
    if ([attribute isEqualToString:NSAccessibilityDescriptionAttribute]) {
	ImageMap *imageMap = parent;
	NSDictionary *info = [imageMap infoForHotSpotAtIndex:index];
	NSString *description = [info valueForKey:@"alt"];
	if (description == nil) {
	    description = [info valueForKey:@"title"];
	}
	return description;
    } else {
	return [super accessibilityAttributeValue:attribute];
    }
}

FauxUIElement objects themselves do not know anything about the actions they will support. That is why the base class implementation of action-related methods didn't do anything. Now that we know HotSpotUIElement objects act pretty much like buttons, we can override the accessibilityActionNames: method to pass back the predefined action name, NSAccessibilityPressAction. The default action description is returned using the helper function, NSAccessibilityActionDescription(). When asked to perform the press action, the HotSpotUIElement object calls up to the ImageMap.

- (NSArray *)accessibilityActionNames {
    return [NSArray arrayWithObject:NSAccessibilityPressAction];
}

- (NSString *)accessibilityActionDescription:(NSString *)action {
    return NSAccessibilityActionDescription(action);
}

- (void)accessibilityPerformAction:(NSString *)action {
    ImageMap *imageMap = parent;
    [imageMap performActionForHotSpotAtIndex:index];
}

Overriding the isEqual: and hash: Methods

One final item to note about the FauxUIElement and HotSpotUIElement classes is how they each override of the isEqual: and hash: methods. This is done to properly support hit-testing on hotspot elements. When the ImageMap class is asked to provide an array of its child elements (the NSAccessiblityChildrenAttribute), it creates an array of HotSpotUIElement objects on the fly. Since the array and its contents are created on demand, and because the default isEqual: method is a simple pointer comparison, an operation that compares two HotSpotUIElements will fail.

So, instead of a simple pointer comparison, the FauxUIElement and HotSpotUIElement class provide overrides of isEqual: (and therefore, hash:). The override method first examines the class, to make sure it is comparing objects of the same type. Then, the role, the parent element, and the HotSpotUIElement's index are compared to see if the objects are the same.

Summary of Access Enabling a Custom View

Let's review the steps we took to access-enable the ImageMap custom view:

  • We provided overrides of NSAccessibility methods to
    • Make sure the view is not ignored (accessibilityIsIgnored:).
    • Make sure the view reports its role and role description correctly (accessibilityAttributeValue:).
    • Make sure child user interface elements are reported correctly (accessibilityIAttributeValue:).
  • We created a generic class called FauxUIElement to represent a child user interface element in the accessibility hierarchy when no corresponding class exists in the class hierarchy.
    Reuse of FauxUIElement is encouraged in those cases where it is necessary.
  • We created an informal protocol called FauxUIElementChildSupport so our "fictional" elements can communicate with their parent.
  • We created a subclass of FauxUIElement to hold logic specific to our application (specifically, logic pertinent to being a hotspot element of our ImageMap class).

Bundled Accessibility Tools

You can use the Accessibility Inspector application to inspect your application's accessibility hierarchy. Accessibility Inspector is part of Apple's Developer Tools suite. It is located in the /Developer/Applications/Utilities/Accessibility Tools folder.

With Accessibility Inspector you can view the entire accessibility object hierarchy for any element under the mouse. The tool shows all of the attributes supported by the element, along with their value. You can even use it to test how your application will respond when an assistive application tries to control your user interface through accessibility object actions.

See the chapter on Using the Accessibility Inspector in the Accessibility Overview documentation in the ADC Reference Library.

Another tool called Accessibility Verifier will display and perform tests on the accessibility hierarchy for all objects in an application. This tool is also located in the /Developer/Applications/Utilities/Accessibility Tools folder.

You can find more information on Accessibility Verifier in the ADC Reference Library.

Troubleshooting Accessibility

One quick way to help troubleshoot your application's accessibility support is to set the flag NSAccessibilityDebugLogLevel. You can pass this flag on the command-line, or, you can set it in Xcode. When running your application from Xcode, if the flag is set, accessibility debugging information will be sent to the console. To set the flag in Xcode, select your application's executable in the project window and open its Inspector window. Select the Arguments tab, and add NSAccessibilityDebugLogLevel 1 to the list of arguments. Figure 1 shows a the inspector window for the ImageMapExample application's executable.

ImageMapExample Executable Inspector Window

Figure 1: ImageMapExample Executable Inspector Window

Table 1 shows some common problems with accessibility support, along with their probable cause.

Symptom Cause
Ignored interface elements The default implementation of accessibilityIsIgnored: returns YES.
Make sure your custom class returns NO.
Setting an attribute on an ignored element Use helper functions to get at the unignored elements:
  • NSAccessibilityUnignoredDescendant
  • NSAccessibilityUnignoredAncestor
  • NSAccessibilityUnignoredChildren
  • NSAccessibilityUnignoredChildrenForOnlyChild
Children do not appear in Accessibility Inspector This can be caused by a failure to properly implement hit-testing.
Make sure your class returns itself or a more specific non-ignored child element.
Child not found If a child element reports an element as its parent, then the parent element must report that element as one of its children.
"Only the first child works" If you use the FauxUIElement class in your own applications, make sure you correctly override the isEqual: and hash: methods.
Remember that elements with the same role and parent are indistinguishable.
Failure to account for isFlipped. Some controls flip their coordinate system so that the origin is in the upper-left corner. Accessibility always uses the bottom-left corner.

Table 1: Accessibility Symptoms and Causes

For More Information

Posted: 2006-10-16