On Sep 16, 2009, at 1:35 PM, Xiang Cao wrote:
Hi All,
I’m working on making our app accessible to voice over on snow leopard. Our app has a very complicated custom view. Basically, the only standard thing is a NSWindow which holds a NSView which holds all the custom elements. So I created my own accessibility object from NSObject and fully comply with NSAccessibility protocol. Every thing works fine, except it’s a little slow.
For example, when I move the VO cursor with keyboard to from one deepest element to another, I have to wait almost a second. I think this may due our huge custom view hierarchy, although most of them are ignored.
I traced some logs on accessibilityAttributeValue and I found it’s being called TOO frequently. For example, if I move the VO cursor with keyboard to from one deepest element to another, the title attribute of one single element is being called three times. I wonder why the voice over need to fetch the attributes for multiple times for a single operation? And I also found no matter what kind operation I did with voice over (move, interact), the voice over will search the entire hierarchy from top level for children for N times (N>10).
I see there are three new functions are introduced with Snow Leopard:
- (NSUInteger)accessibilityArrayAttributeCount:(NSString *)attribute
- - (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount
- - (NSUInteger)accessibilityIndexOfChild:(id)child
It seems they are designed to make the attribute fetching more efficient. But I’m not sure how to implement them. After engaged these functions, the voice over cannot get the proper elements.
The new methods can improve performance especially in cases where the default AppKit behavior is to get the full array of values, and then perform the operation. In the case of custom elements, where you often create temporary objects, this can increase performance a good deal.
Not all of the methods are appropriate in all situations. Take the example of a custom view that is an AXList, and the children of the AXList are all custom elements.
By default AppKit will do the following when asked for the count of AXChildren for instance:
1. Get the array of AXChildren.
2. Return the count of the array.
If you already know the count of the children, you can override -accessibilityAttributeCount:, and if the attribute is AXChildren, just return the count. But if it an attribute that you do not handle, then be sure to return the result from super.
You can implement this for any attribute whose value is an array.
Let's assume that you identify the children of the array with an index. In order to tell your custom NSAccessibility objects apart you store that index in the custom object.
If -accessibilityArrayAttributeValues:index:maxCount: was called with the attribute AXChildren, you would typically only need to create a subset of the entire array of children, for those indexes requested.
Again, be sure to call super to allow the default implementation to work, for any array-based attributes you do not handle specially.
Similarly, if you implemented -accessibilityIndexOfChild: you could just pull the index value out of the child object and return it, without generating the entire array of children and then searching in it for the correct index value.
Also note that these methods were made public in SnowLeopard, but have existed privately since 10.2. If you implement them, they will get called on previous versions of Mac OS X as well.
Another problem I encountered is with mouse moved VO cursor. When I set the VO cursor follows the mouse cursor, strange things happened. The hit test works great. It is enable to find the deepest element under my mouse. But when I move my mouse from one element to another, the voice over start to read the new element very quickly, but the vo cursor has not been moved to the new element. After 0.5 second of computing, the vo cursor finally moved to the new element but the voice over starts to read it again and the first reading is cut. So it sounds like a double hit every time. I’m not sure what I did wrong. Because other app does not behave like this.
Any ideas? Thanks.
In general you might try setting NSAccessibilityDebugLogLevel 1, as a runtime argument to your app. -NSAccessibilityDebugLogLevel 1
-James
Xiang
--------------------------------------------------
James Dempsey
AppKit Engineering
Apple