James,
My text field subclass uses a SRXTweetObject as it's object value, which is a simple data model class that stores information about the tweet.
Here are the results of having one my cells selected in Accessibility Inspector:
<AXApplication: "Syrinx"> <AXWindow: "Syrinx (8 New Tweets)"> <AXSplitGroup> <AXScrollArea> <AXTable> <AXRow: "[Nik Fletcher]"> <AXTextField: "Nik Fletcher">
Attributes: AXRole: "AXTextField" AXRoleDescription: "text field" AXHelp: "Timeline Tweet" AXValue (W): "Tweet Cell" AXEnabled: "true" AXFocused (W): "false" AXParent: "<AXRow: "[Nik Fletcher]">" AXWindow: "<AXWindow: "Syrinx (8 New Tweets)">" AXTopLevelUIElement: "<AXWindow: "Syrinx (8 New Tweets)">" AXPosition: "x=1090 y=926" AXSize: "w=771 h=88" AXChildren: "<array of size 0>" AXSelectedText: "(null)" AXSelectedTextRange: "(null)" AXNumberOfCharacters: "10" AXVisibleCharacterRange: "pos=0 len=0" AXInsertionPointLineNumber: "(null)" AXPlaceholderValue: "(null)" AXDescription: "Nik Fletcher"
Actions: AXShowMenu - show menu AXConfirm - confirm
Parameterized Attributes: AXLineForIndex AXRangeForLine AXStringForRange AXRangeForPosition AXRangeForIndex AXBoundsForRange AXRTFForRange AXStyleRangeForIndex AXAttributedStringForRange
Warnings: AXDescription: Unable to determine if the attribute is settable (kAXErrorFailure)
Optional Attributes: AXLinkedUIElements AXValueDescription AXSearchButton AXTitleUIElement AXClearButton AXSelectedTextRanges AXSubrole
And for this cell VO reads "Tweet cell Nik Fletcher". The "tweet cell" I assume it gets because that's what the title of the cell is set to in IB. The name "Nik Fletcher" is returned from my NSAccessibilityDescriptionAttribute. Preferably I would like it to read optionally <Direct Message from>, <Mention from> or <Retweet from> then <Name>,<Tweet> and optionally <Date>, which is what I had been doing originally.
_______________________
Mickey Roberson
MRR Software
On May 11, 2010, at 6:12 PM, James Dempsey wrote: On May 11, 2010, at 1:38 PM, Mickey Roberson wrote: James,
Thanks for the advice. My code now looks like this:
- (NSArray *)accessibilityAttributeNames { NSMutableArray* axAttrs = [NSMutableArray arrayWithArray:[super accessibilityAttributeNames]]; [axAttrs addObject:NSAccessibilityDescriptionAttribute]; return axAttrs; }
- (id)accessibilityAttributeValue:(NSString *)attribute { if([attribute isEqualToString:NSAccessibilityHelpAttribute]) return @"Timeline Tweet"; if([attribute isEqualToString:NSAccessibilityDescriptionAttribute]) { SRXTweetObject* tweet = (SRXTweetObject*)[self objectValue]; NSMutableString* readableString = [NSMutableString stringWithString:[tweet.user displayName]]; //Removed for brevity... return readableString; } return [super accessibilityAttributeValue:attribute]; }
However, doing it like this, when I enable VO, it only reads what I put in for the NSAccessibilityDescriptionAttribute. I thought it would read either a description of the object I use for setObjectValue: or read at least one of my other text field cells. My NSTextFieldCell subclass is composed of multiple text field cells, so I feel like I must be missing something obvious in the documentation, but what exactly does VO try to read for my cell, and how do I change it to read what I want?
Mickey - Is your text field cell subclass one that pops lots of info into a dictionary which is the object value of the cell?
Could you focus on your text field cell in Accessibility Inspector, copy and paste the inspector window contents, so I can see what is being reported for the various attributes?
Thanks.
-James
_______________________
Mickey Roberson
MRR Software
On May 10, 2010, at 8:17 PM, James Dempsey wrote: On May 10, 2010, at 2:57 PM, Mickey Roberson wrote: I have implemented custom table cells (subclass of NSTextFieldCell) in my application (a Twitter client). Each cell represents a tweet including things like name, date, tweet text, etc. Within my cell subclass I have the following code:
- (NSArray *)accessibilityAttributeNames { if(validAXAttributes == nil) { validAXAttributes = [[NSArray alloc] initWithObjects:NSAccessibilityRoleAttribute, NSAccessibilityRoleDescriptionAttribute, NSAccessibilityHelpAttribute, NSAccessibilityFocusedAttribute, NSAccessibilityParentAttribute, NSAccessibilityChildrenAttribute, NSAccessibilityWindowAttribute, NSAccessibilityPositionAttribute, NSAccessibilitySizeAttribute, NSAccessibilityEnabledAttribute, NSAccessibilityValueAttribute, nil]; } return validAXAttributes; }
- (id)accessibilityAttributeValue:(NSString *)attribute { if([attribute isEqualToString:NSAccessibilityRoleAttribute]) //AXRole return NSAccessibilityStaticTextRole; if([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute]) //AXRoleDescription return @"Timeline Tweet"; if([attribute isEqualToString:NSAccessibilityHelpAttribute]) //AXHelp return @"Timeline Tweet"; if([attribute isEqualToString:NSAccessibilityValueAttribute]) { //AXValue SRXTweetObject* tweet = (SRXTweetObject*)[self objectValue]; NSMutableString* readableString = [NSMutableString stringWithFormat:@"%@: %@", [tweet.user displayName], tweet.text]; //Removed for brevity... return readableString; } return [super accessibilityAttributeValue:attribute]; }
Mickey - it's great that you are working towards making your app accessible! That's great!
You should always ignore NSAccessibilityException exceptions - and if you have special exception-handling code, you should always re-raise NSAccessibilityExceptions. They are expected to occur even if there is no problem in your application.
Why? For historic reasons, AppKit uses exceptions internally to deal with situations where an application needs to send an error back to the accessibility client.
So, you can and should safely ignore those exceptions.
Some other things to point out, scanning through the code you provided:
1. When subclassing a text field cell, it is best to let the NSAccessibilityValueAttribute keep its inherited behavior (which reports the string value of the text field). This is because there are a number of other attributes and parameterized attributes, such as AXNumberOfCharacters, AXBoundsForRange, AXAttributedStringForRange, AXStringForRange, etc, which all are based on the value of the text field.
If you return something else as the AXValue, it will confuse accessibility clients like VoiceOver.
Instead, add an NSAccessibilityDescriptionAttribute. This string can contain all of the extra information.
2. If the cell is not editable, it should already report its role as static text, and its role description should be provided automatically as well.
3. In this case, you should not provide a custom role description - the role is static text, and the element should describe its role accordingly.
4. You might want to consider getting the attributes from your superclass, and adding the additional attributes. Otherwise, if an attribute is added to text fields in the future (For instance we added AXPlaceholderValue in a recent release), then your subclass will not support it automatically.
-James
For the most part this behaves correctly except that it occasionally reads the Accessibility Content twice. While looking into this in gdb I noticed that multiple NSAccessibilityExceptions are being thrown when I turn VO on with a tweet as main focus.
2010-05-10 17:39:43.954 Syrinx[85907:a0f] NSExceptionHandler has recorded the following exception: NSAccessibilityException -- Attempt to observe "AXTextInputMarkingSessionBegan" on non-observable element: <NSTableViewCellProxy: 0x11d4ebbf0> col:0 row:33 real element:<SRXTableCell: 0x11d51e5a0> Stack trace: 0x7fff81c18a2c 0x7fff827510f3 0x7fff82ed19b9 0x7fff86485c45 0x7fff864839f5 0x7fff8618900b 0x7fff86192f6c 0x7fff8616f541 0x7fff82e16201 0x7fff82e148df 0x7fff85349ada 0x7fff853498df 0x7fff85349798 0x7fff86235a2a 0x7fff86235379 0x7fff861fb05b 0x7fff861f3d7c 0x100001365 0x100001314 0x1 (gdb) continue (gdb) continue 2010-05-10 17:39:44.641 Syrinx[85907:a0f] NSExceptionHandler has recorded the following exception: NSAccessibilityException -- Attempt to observe "AXCreated" on non-observable element: <NSTableViewCellProxy: 0x11e673ac0> col:0 row:33 real element:<SRXTableCell: 0x11d51e5a0> Stack trace: 0x7fff81c18a2c 0x7fff827510f3 0x7fff82ed19b9 0x7fff86485c45 0x7fff864839f5 0x7fff8618900b 0x7fff86192f6c 0x7fff8616f541 0x7fff82e16201 0x7fff82e148df 0x7fff85349ada 0x7fff853498df 0x7fff85349798 0x7fff86235a2a 0x7fff86235379 0x7fff861fb05b 0x7fff861f3d7c 0x100001365 0x100001314 0x1
What am I doing wrong that is causing these exceptions, and how do I fix them? _______________________ Mickey Roberson
MRR Software
-------------------------------------------------- James Dempsey AppKit Engineering Apple
|