Application Technologies • 1:18:17
Learn how to make your Cocoa application accessible to users with disabilities. We'll cover the Accessibility API, provide you with practical guidance on how to use the Universal Access features - like Zoom and VoiceOver, and show you how your application can work with specialized input and output devices. You'll also find out about valuable testing techniques and verification tools to ensure your application is accessible.
Speakers: James Dempsey, Rick Fabrick
Unlisted on Apple Developer site
Transcript
This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.
Your custom views and your custom controls fully keyboard navigable is very important to accessibility. Someone who's unable to use the mouse needs to use a keyboard to get around. It's very, very important that they're able to do things with a keyboard. So for custom views and controls, you want to implement full keyboard navigation.
And then the other thing to think about as you're designing the user interface of your application is you don't want to have things that require a mouse, and the mouse is the only way to get things done. And kind of the canonical example there is in the Finder for many years, the only way to copy or remove a file was to drag it from one location to another, which, if you can't use a mouse to drag, that's very, very difficult. That's impossible.
So by adding copy and paste menu items that can be gotten to through keyboard navigation, the Finder is now much more accessible when it comes to manipulating files. So in your applications, those are things to think about. We're not going to focus a great deal on that anymore in this session, but I do want to emphasize that this is a very, very important point before we go on.
Now, most of the time when you hear about making your application accessible, it's talking about how your application interacts with assistive applications. And we're going to define that term in just a moment. That's really the bulk, that's the crux of what we're talking about today. So let's jump in and talk about that a little bit more. So we have some applications running on our system. We have Finder, it's a Carbon app.
We got a Cocoa app like iChat. We have your Cocoa application running. And they're all running as applications do. They're drawing to the screen. They're handling mouse events. They're handling key events. They're all happy. They're in their little snug, protected memory space chugging along. And then somebody launches an assistive application, in this case, VoiceOver, the screen reader on Mac OS X.
An assistive application at a high level is an application that interacts with other applications on the system and can provide an alternate way for a user to interact with all the other applications on the system. The more nuts and bolts definition is that it's an application that is using a set of APIs called the Accessibility APIs. We're not concerned with those APIs because we're not writing an assistive application, but it's important to know who's going to be asking you questions and interacting with your app. They're a set of C APIs.
They start with the AX prefix. If you search in the developer docs for accessibility, you're going to find them. They're parts of the API that you don't have to worry about. Now, using those APIs, and they're public APIs so anyone can write an assistive application, using InterProcess Communication, it's going to be able to talk to all the other applications running on the system.
And all those other apps are going to respond back. And so this is kind of a line of communications that's beyond the one that we usually think about when we think about an application being interacted with by a user. It's another way for somebody to interact with your application. Now, how is it that my application is able to do this? I didn't do any accessibility work whatsoever. Well, both Carbon and Cocoa frameworks have baked into them the infrastructure to be able to listen for this communication from assistive applications and respond back.
Now, We often will call these other applications on the system that can interact with an assistive app, you'll hear them call things like an Accessible Application or an Accessorized Application. So, I wanted to get those terms straight because it's important to know what side of the communication you're on. And we, in this session, are talking about your application as kind of the thing listening for and responding to queries from assistive applications.
Now, there can be many, many assistive applications. What I'd like to do is - some of you may not have seen VoiceOver in action - is bring up Rick Fabrick. He's a senior engineer on VoiceOver. To give you a brief demo of VoiceOver and also to talk about some of the new features that are coming out of it. Thanks, James.
Good afternoon everybody, my name is Rick Fabrick. I am an engineer on the VoiceOver team. And what I'm going to be doing for the next 5 or 10 minutes is two things. I'm going to be going over some of the basics of VoiceOver, and then I am going to go and demo some of the new features we plan on having in Leopard.
So VoiceOver. VoiceOver is an assistive application. In this case, it comes built in to the operating system Mac OS X. It is a screen reader, so it allows users to interact with their computer without requiring a mouse or a display. So the base system for a VoiceOver user is the CPU, or the computer, and the keyboard. So let me turn VoiceOver on. As James may have mentioned, one way to turn it on is to go to the System Preferences.
Can we have the other machine up? And in the System Preferences that will be appearing very soon, there we go, thank you. You go to the Universal Access pane, and the top left you press the On radio button. Welcome to Macintosh. VoiceOver is running. System Preferences, Window, Universal Access back button. Now, when VoiceOver first launches, it gives you some information about the state of the machine, which application is front most, what window you're in, things like that.
When we first started designing VoiceOver, one of the main things we wanted to do was make sure that if a VoiceOver user was working with somebody else at the computer, they were collaborating, that they could understand what each other was doing very easily. So VoiceOver offers some cues, some visual and audio cues, to help that along.
Now, one of our visual cues is what we call the VoiceOver cursor. And what that does is it focuses on the element that the user is currently "looking at." And it's the element that they can interact with. So, let's say I'm using VoiceOver. Now, one of the features that VoiceOver provides is in a way to increase the size of the cursor. So, now this is useful for the VoiceOver user if they do have some vision, but it's limited. But it also helps with collaboration.
So, what I'm going to do is I'm going to send a command to VoiceOver, and I do that with the control and option keys down. With those two keys down, any other keyboard sends a command to VoiceOver. So, VoiceOver 3, 4, 6. You may be able to see in the top left, the VoiceOver cursor. And the VoiceOver cursor is quite large. It's those black rectangle around the element.
Okay, so now with VoiceOver, the control and option keys down, and the arrow keys, I can move the VoiceOver cursor to see what is in the window. So I can tell what I can do. Let me move the cursor over. Forward. Dim button. Show all button. And as the cursor moves from element to element, VoiceOver describes the element so I know what I can do. So in this case, I'm on the Show All button. And with VoiceOver, I can press that button.
Press Show All button. And I can now move the cursor to the computer without using a mouse. Let me bring the VoiceOver cursor back down to normal size. VoiceOver 4, 3, 2, 1, 1. Now, as a developer, you should be looking at one of the features that is in Tiger, and that is the screen curtain. What that does is it dims the screen so that you get a better sense of how VoiceOver users will be interacting with your applications.
Let me turn that on just so you can see. Screen curtain on. And now I can use VoiceOver normally by navigating. Forward. Dim button. Back button. And this is a tool you can use to possibly find areas in your application that may need more accessibility work. So let me turn the screen curtain off. Screen curtain off.
Okay. Hide system preferences. Finder. Leopard selected. So let me go into a couple of the features that we're going to be having for Leopard. The first one deals with - let's see. System terminal preview. Preview window. The VoiceOver commands themselves. So another thing when we were designing VoiceOver that we wanted to make sure was very easy is once the user learned how to use VoiceOver, the user learned where the commands were, that they can go to any Mac OS system running Tiger Relator and be able to use it using VoiceOver. And whether that's in the library or a school setting. So - and that's going to be true for Leopard as well. So we're not going to be touching the main keyboard.
But what we'd like to do in Leopard is take advantage of the numeric keypad on the right-hand side of an extended keyboard. What we're going to do is allow the user to program their most used VoiceOver commands to the keys over on the right-hand side. And we're also not going to require the control or the option key.
The option key is to be pressed. So you can do pretty much anything you want to do with VoiceOver using one hand very quickly. What's nice about this is all the keys are very localized, so you don't have to be moving around a lot. So I can - Next. Dint. Button.
Zoom out. Button. Zoom in. Button. Navigate the VoiceOver cursor. If I move my finger down just slightly to another key on the numeric keypad - Press. Zoom. Press. Zoom in. Button. I can interact with the button. So I can pretty much do anything I want and program it to the way that I use my computer. Okay. So that's the first feature. The second feature deals with navigation. High preview. Finder. Leopard selected. Okay.
In Tiger, I can navigate the VoiceOver cursor up, down, left, right, to the first visible element, to the last element in the window. In Leopard, what we want to do is make navigation quicker and allow you to jump to just the types of elements that you want to go to.
Let's say you're just interested in the images in the page or the window. You can jump from image to image. The same thing for links. Also, text attributes like bold. You can jump from one bold block of text to another, italic, things like that. So let me demo that with a text edit document. Preview. System terminal. Text edit. Text edit. Window.
Okay, so here I have a document, and let's say that I want to read all of the text in the document that is bold. So with one key combination, I can... Accessibility. A new voice. Built for Braille. Closed captioning. Jump from section to section. So in this document, each section is titled with some bold text, and I can quickly jump from section to section in this document with the bold command. Italics command, so I can jump to the next block of text that is italics.
All features referenced in the Mac OS X Leopard sneak peek are subject to change. In this case, it's at the very end of the document, so I jump down. I can also jump...navigate backwards to graphics. Embedded GIF file. Embedded PNG file. So this should make navigating to just the things you want to see in the window very, very easy. Alright, so the last feature that I want to demo... Hide text edit. Finder. Leopard. Selected. Can be demoed in Terminal. Text edit. Preview. System preferences. Terminal.
Alright, so in Terminal, if I'm doing some... Command line commands, and I hit return, the command may print out some output. A VoiceOver user in Tiger had to navigate explicitly back to see what just came out. In Leopard, that's not going to be true. If I type ls return... Desktop music sites, documents, pictures, our library, public movies, send registration, HW0014511426IF, Apple. Okay.
So... VoiceOver will automatically speak out whatever just came out. So you don't have to go back and forth and find out. That's been a feature that was greatly requested in Tiger. And not only in Terminal is that useful, but in Console and it's very useful in iChat. So if you're chatting with a buddy and somebody sends you a new message, you don't have to go back and forth or go back and say, "What did they just say?" We'll automatically speak it for you.
Okay, so that's all the time I have today for features. What I'd like to do is ask James to come back up so he can explain to you how you can accessorize your application so that VoiceOver users can take advantage of all of your cool features. Thanks. Thank you.
Thank you very much, Rick. All right, so we'll go back to our little diagram here. So you just saw VoiceOver interacting with lots of other applications on the system. It's an example of an assistive application. It's not the only. In fact, those other Apple technologies that I mentioned - Apple X-Ray Master Track, the Watch Me Do feature in Automator - and anything that's getting accessibility information is using those APIs.
So we've talked a little bit about that communication, but not so much about what kind of information is coming back and forth and what form does it take. So let's dive in a little bit deeper. Let's simplify our diagram to just one assistive application and a Cocoa application. And using those AX APIs, an assistive application basically gets a process ID and passes it into those APIs, and it gets back a little lightweight. UI Element, or more formally an AX UI Element ref.
This is just a little lightweight thing that represents a UI element. In fact, this first one that comes back represents the application on the other end of this communication here. We don't package up NS applications and NS views and send them to other processes. That would be way too heavyweight. And we also don't want assistive applications to need to know about HI views and NS views and different things and different frameworks. So, we have a general UI element that we use.
And once an assistive app has hold of this UI element, it's able to ask it some questions. Like, "Give me your list of attributes." "Give me a value for a particular attribute." "Is that attribute settable?" And then it can set a value for an attribute. And with this small kind of set of functionality and this one generic UI element, we're able to do quite a lot.
So let's focus in on a UI element and some of its attributes. So that's the one that that assistive app got back. And some very key attributes, types, are the role. In this case, it's just a string that says I'm an application. And it also has children. And that children attribute, when you ask for its value, will give back an array of other UI elements.
So we can build an entire hierarchy fairly easily. And then that first one, it has a role, which is - this represents the menu bar of the application that I'm talking to. It has a parent, which refers back up to the application UI element. And then it has children of its own.
And then this other one, perhaps, is a window in that application. Again, the AX role is referring to kind of the type of UI element, or the kind of UI element that you're talking about. And then the AX role is referring to the type of UI element, or the kind of UI element that you're talking about. And then the AX role is referring to kind of the type of UI element, or the kind of UI element that you're talking about.
And then the AX role is referring to kind of the type of UI element, or the kind of UI element that you're talking about. element or the kind of UI element that this represents. It has a reference up to its parent, again the application, and then an array of children of its own.
So, rather than go through slides, let's come on way over here. It's a big stage. Primany to the demo machine and let's start our first hands-on. Okay, how many folks are actually following hands-on with me? Okay, good. Excellent. So what you will want to do is in the dicey bunch of stuff, In the folder, you'd want to go to stage one. And first, let's launch the application. It's labeled Dicey Start.
So Dicey is just a little game, a little dice game where we can roll the die, We get a bunch of items here. We can click a die to hold that die so that when we re-roll, those values are held. I'm going to try for fives. No more fives. Oh, I'm going to go for a full house.
Oh, nothing. Oh, I got it. That's excellent. So your results may vary. Yes, please. Yeah, all right. Full house. And then this column here is showing me if I were to choose to score in this particular category, I'd get 25 points. This button kind of locks in that score, and I'm ready for my next turn. So feel free to play with Dicey for just a second or two, and then stop and pay attention.
I didn't realize until too late that potential pitfall of giving people a game that I know when building it I tested way too much. Oh, I'm doing great. I'll have to save this for now. Okay. I digress. So, what I'd like you to do next - oh, one other thing to show you in Dicey is if you would open up the Preferences pane.
I had some preferences set. You'll notice that we have a game difficulty slider that goes from difficult, which means I only get one roll per turn, normal is three rolls per turn, and easy is five rolls per turn. And we'll be accessorizing this slider a little later. I'd like you to leave these two open for now. Everybody with me? Screen pretty much look the same? Show of hands? Wave them. Thank you. Next thing I'd like you to do is let's go into System Preferences. And we're going to go to the Universal Access Preference pane.
And once there, I want you to make sure that you have checked "Enable access for assistive devices." Everybody got that checked? All right, I'll wait a couple seconds. Now does everybody have it checked? Show of hands? Thank you. It's hard to see nodding up here. Great. So close up system preferences. That checkbox is what allows assistive applications to communicate with other applications on the system.
And then the last thing we'll do here is I'd like you to navigate to slash developer. Actually, I'll go into column view. So developer applications Utilities, Accessibility Tools, Accessibility Inspector, and for our session today I'd like you to drag that to the dock because we'll be using that a good deal. So the path again, in case you missed it the first time: /developer/applications/utilities/access ibilitytools/accessibilityinspector.
Everybody got that in the dock? Show of hands? Thank you. All right. And with that, you can close your Finder windows if they're getting in your way. Maybe I'll just hide them. And then launch Accessibility Inspector. And I'm going to wait for a second and then bump up the font size here.
All right, and as I move the mouse around, Accessibility Inspector is itself an assistive application. It's communicating with other apps on the system. In fact, if I move it around Dicey, I notice that I'm getting in the window a lot of information back. I'm seeing the representation of the UI element that is under the mouse at any given time. I'm seeing all of its attributes, so we'll see some familiar ones up there like roll, which we saw just a bit ago, kind of midway down as the parent.
It doesn't have any children of its own, but many, many more attributes including where it lives on the screen, its size and position, its value, whether it's enabled, whether it has the focus, what window it's in, the top level UI element, which represents if the thing happens to be in a sheet or a drawer, it will report that instead of the window that the sheet or the drawer belongs to.
And then at the very top, we can see its hierarchy from that text element all the way back up the chain of parents to the application. What I'd like you to do is please point anywhere in the blank spot of the title bar of the the Dicey window, and let's click on it to make sure it's in the front.
And then what I'd like you to do is we're going to freeze the UI - excuse me, freeze on a particular UI element. If you look at the small print at the very bottom of the Accessibility Inspector window, it says "Command F7 toggles UI element lock." On a laptop, you typically also have to hold down the Fn key, so Command Fn and then F7, and we should lock onto one UI element, so that as we move the mouse around, things don't change like crazy in that text field.
Does everybody have it locked? Please raise a hand. Thank you. Excellent. So it's red and now we can kind of take a moment and look around here. When we locked, we also did one other thing. You'll notice that a little window has popped up. Let's see if I can zoom in here. There we go.
And if we take a look, it shows us a pop-up of all of the attributes that we see in the bigger Accessibility Inspector window. It also allows us to highlight the item that is currently locked on. And by doing that, it's shaded in red. So that way, if you're not sure where you are, you can get a visual depiction of where that element is on the screen that you're currently looking at. So feel free to turn that on and turn it back off.
And then also interesting in here is the Go To pop-up. So sometimes attributes are just values such as the size, which is just a point. Or the position, which is just a point. Sometimes an attribute is a reference to another UI element. And this Go To pop-up allows us to navigate or jump around to - or excuse me, navigate is a better word - to other UI elements. Now we have a lot of children, but we are going to jump up to the parent. So the Go To AX parent. And when I release this, we're going to jump to the application. All right.
Let me zoom back out. And so if I take a look at the application, you'll notice again it has a role. The role description is an attribute that provides a localized, presentable description of the role. The title of the application is Dicey. We notice it has three children.
And it has kind of convenience attributes for jumping right to the menu bar and right to its windows instead of going through its children. You'll also notice we see whether we're frontmost or not and whether we're hidden or not. And that these attributes have this W in parentheses. That indicates that we can set them or that they're writable. And we can actually interact with the application right here in Accessibility Inspector. So going back down to our Locked On panel, I'd like you to, in the attributes pop-up, choose Hidden.
And then we're going to type the new value here. Accessibility Inspector is going to expect a 1 for true. So we'll type a 1. And then click "Set Value." Is everybody there? Show of hands. Excellent. So, once we set that value, now "Hidden" is indeed true. That application is hidden. Our Accessibility Inspector window hasn't refreshed. We need to click the Refresh button down here. And we'll notice that Hidden is now True, or reports itself as True, and Frontmost reports itself as False, because some attributes are interrelated.
Now, Let's go back to Attributes, to AX Frontmost, and we're going to set that to True to bring our application to the very front. So Attribute, AX Frontmost in the pop-up. One for true. And then we're going to set the value of AX_FRONTMOST to true. Is everybody there? Excellent. All right. Let's refresh one more time just to notice that the values do indeed update.
And now let's navigate down to the Preferences window. So using the Go To pop-up, we'll look at the AX Children, or we could actually go to the AX Windows, your choice, of Dicey, of the application. And we want to go to the AX Window Preferences. So Go To Preferences.
Everybody there? Show of hands. Thanks. And let's turn on highlighting now so we make sure that yes, we are indeed looking at the right thing. And you'll notice that Window has a number of things that an application does not have, including ways to very quickly get to the controls, such as the Close button. So let's go to the Close button. So go to AXCloseButton.
And you'll notice that our highlight updates and our focus updates. And if we look at the bottom of all those attributes, you'll notice that there's an action listed. So in addition to having attributes, a UI element can have actions. And actions tend to be things that are best represented as like a single mouse click, a single mouse action. And notice that it's representing what you're doing to the mouse.
It's not a close action, it's a press action. Because every button you can press it, what actually happens when you press it isn't part of the definition of what we're exposing here as the action. So down in the Locked On area, you'll notice that we have a pop-up for actions. AX Press is our only choice. And we can click to perform the action, which should close. our window. So let's click perform.
and our window goes away. All right, there's going to be one more thing that we take a look at before we move on. First, we're in this state where this thing's red and locked on. Let's untoggle by doing Command and then probably on a laptop Fn, F7 to unlock the UI element.
So now as you move the mouse around, things change. If you don't have any dice showing at the moment, I'd like you to roll the die. And we use Accessibility Inspector to, as you might guess, inspect the accessibility of an application. And one thing we'll notice is, you know, I point to various text, and it reports itself as static text.
Or I point at the roll button, it tells me it's a button and it has an AX title of "roll." But then I point at this custom dice view and... nothing. As far as an accessible - excuse me, an assistive application is concerned, there is no dice view in this application. And to have a dice game without a dice view makes this very unaccessible.
Now two other things that we'll be dealing with are if we point at one of these arrow buttons, you'll notice that its title is blank because it doesn't have a text title. So a user would have no idea what this button might do. I can press it, but who knows what's going to happen.
And even if I kind of point at the total down here, the numeric total, being excited, I can see that the grand total is on the same line as this. But from the standpoint of the accessibility inspector, there's no connection between this number and the label grand total.
So some of the things we'll be doing in this session, or the things we'll be doing, is making it so that those items that are already reporting some accessibility information report more and better information. And then we will also be making sure that this invisible view that, as far as voiceover would be concerned, it doesn't exist, we'll make sure that it shows up on the radar map and reports itself correctly. Okay, great. Let's go back to slides.
Let me hide this for a sec. Okay, so that was an application where I built the application, I did no accessibility whatsoever, and as you saw, we were able to interact a good deal with the application, but there were some key portions that were completely inaccessible. So Cocoa is doing a lot of that for you.
Now, a few other things that we saw in that hands-on is, in addition to asking about attributes, We might also be asked about a list of actions on our side of the - I'll move over to the Cocoa side - a description for an action, to perform an action.
We also saw that moving the mouse around, there's some way that an assistive app is finding out what element is at a given point. So there must be some hit testing going on in Cocoa. And in addition, one thing we did not see, but there's also the concept that an assistive app can get some notifications that things have changed in the application.
So that's what's going on. How is Cocoa doing that automatically? And what is it on the Cocoa side that's the backend, kind of the thing that that UI element is referring to?
[Transcript missing]
So we also need to do hit testing and focus testing. So hit testing, we get past a point and we return the element that corresponds to that point, and also what element in the application has the focus.
So far, nine methods. There's a tenth method, and it also explains why our Dice View just didn't show up by default. Accessibility is ignored. The view hierarchy is more complicated than even as a user looking at an application with a window and a button in it. You can't necessarily even see that that window has a frame view with a content view, and then there's an NSButton, and then in that is an NSButton cell.
You just see a window and a button. So similarly, we don't want to force an accessibility or an assistive application to deal with that complexity either. So we only report the window having a button. Everything in between It's part of the view hierarchy, but it reports accessibility is ignored, yes. In fact, that is the default for a view, or NSView. Accessibility is ignored, yes, I am ignored.
Now, on the other hand, sometimes we need to provide more detail to an assistive application than actually exists in our object model. So, for instance, a scroller It's a scroller. However, if you inspect a scroller in... Accessibility Inspector, you'll notice that it has children. It has four buttons and a value indicator.
Now, where did these come from? Well, in fact, they're not cells, they're not controls, they're actually just fictional objects, they're not fictional, they're real objects, but they're objects that we make up expressly for the purpose of responding to the 10 methods of the accessibility protocol so that they can present themselves as buttons and value indicators.
In our case, we have custom sub-elements where each die is a separate class. It knows how to draw itself. It handles a little bit of dealing with drawing the focus and what have you. But it's not an NS cell. It's not an NS view. It's just something that kind of does a little drawing for me.
So, we are going to implement the ten methods of the Accessibility protocol. And we're going to accessorize these as check boxes, because that's kind of the closest role to the standard roles. Because we can hold them or unhold them. We can kind of turn them on or off as to kind of a checked state.
[Transcript missing]
So first, just a very quick look at it, at the project itself. We have an application controller. We have a custom view class, which is doing the drawing of the dice view.
And then within it, a die class, which is responsible for drawing and dealing with the randomization of each die. The game is kind of the back end logic. And then I've added-- A category on DC Dice View in the dcdiceviewaccessibility.m. So if you select that, take a look.
So, the first thing we're going to do is just see what happens if we change the default. Instead of saying that we are ignored, let's uncomment the method "Accessibility is ignored" And recompile and launch. So build and go. Is everybody there? Show of hands. Who's catching up? Show of hands.
Okay, I'll leave that running and I'll repeat. Step 2, DC Dice View, Accessibility. It's a category where we're going to put our accessibility stuff, and we're overriding accessibility is ignored to return no. Everybody got it running? Show of hands. Excellent. And once it's running, I'd like you to launch Accessibility Inspector if you had quit it.
And you'll notice that now, at the very least, we're not completely off the map. Something is here. It doesn't know what our role is, but actually it has a decent amount of information. The size and position is there, it knows what window we're in, it knows who our parent is. So we are picking up the accessibility that NSView implements by default, simply by saying we're not ignored.
Okay, but let's actually go in and at least get our role correct at the moment. So we'll quit Dicey. I'll hide the Accessibility Inspector. And back in the code, we're going to... Return something different for the role attribute. So if I uncomment this second method, which is Accessibility Attribute Value, Simply, if the thing that they're asking for is the role attribute, and again, those constants are all defined in NSAccessibility.h, then instead of returning the default, we're going to say we're a group, because we're grouping a bunch of sub-elements. There's no real interaction I can do with the green area itself. And that role is also defined in NSAccessibility.h. However, if they ask for anything else, I'm just going to do what my superclass has implemented. So let's build and run again.
And bring up the Accessibility Inspector. How many folks are there or very close to there? Okay, great. I'll hold up for just a sec. But what you'll see is that our role is indeed now reported as AX Group, but also the role description has automatically updated for Group. Our superclass implements the functionality to return the proper role description for whatever role we happen to have, so we did not have to implement that ourselves.
Now, we're still not all the way there because, okay, we know there's something here, but these dice are still invisible as far as an assistive app is concerned. But we will get to that all in good time. All right, so that's it for this step. Let's jump back to the slides.
[Transcript missing]
It's pretty easy to take a custom view and at least get it on the map, and that there are a lot of attributes that automatically show up for us. And we've also seen that things like buttons and text fields and the like already have all the accessibility you need, except perhaps for a piece or two here and there.
So let's talk about customizing some instances. So we saw that we had those image buttons that we had no idea what would happen if we pressed them because they had no title. We saw numbers that were just floating in space and we had no idea that it was the grand total because there was no accessibility information as to what title it happened to have.
And to customize instances, try to make it as easy as possible. You don't need to subclass, you just override a few attributes. And you can do it programmatically using the method. So you can set the accessibility, set override value for attribute, and you can also set many of these up using interface builder. And in fact, we're going to do both in just a moment. But first, let me tell you about a few of these common attributes that you definitely want to add to your application.
So the first is the description attribute. This is especially handy on buttons. You could put it on a group if you had a number of groups as to describing what this was grouping. Some rules of thumb: it's just a simple string. It should be a localized string. Don't put the role name in the string. Don't call it the "add button." Just call it "add" as the description. Most assistive applications, such as VoiceOver, are going to tack the role on the end anyway, and it'll say "add button button," and that just sounds dumb.
It often is the same thing that if you had a text title in there, that's what you would have written, but use lowercase. And it's not the role description, which is saying, "Okay, I'm a button, I'm a window." This is more specific to the instance of the item.
Now, Spotlight Help uses the description as it's kind of figuring out where it's going, or as it's indexing your UI. Spotlight Help also uses these title indicators. So this is a reference from one UI element to another, because sometimes the title isn't embedded in the same control. So in this case, we have a few examples here.
The title UI element attribute is essentially saying, I am pointing to the UI element that holds my title. So, the text field that says 2-42 would have that property and on the other end or the value of that property would be the grand total. Or the text field, the blank one, would have the title UI element attribute and its value would be that name text field.
And you can also set up a back pointer where a particular UI element can say, I serve as the title for all of these other UI elements. And that can be an array of UI elements. So it might be that, well, grand total doesn't really work, but it might be one where there are more than one things that this thing refers to. This also is used by Safari help.
Links. So once in a while when you're going through Mac OS X, you'll see an interface where there's a bunch of stuff on the left, and you click on the thing on the left, and the thing in the middle changes to reflect the thing you selected on the left. But there's no good way in accessibility by default to know that this thing that I selected on the left is related to all these other things that just happened over here. To do that, you want to set up linked UI elements. And it's an array of UI elements that are related to or linked to that current UI element.
Then finally, labels. Again, there's a lot of information that looking at a user interface you can discern. I know that that last thing is where difficult is, and that one on this end is where easy is. But that information doesn't automatically show up for an assistive app. And so we need to give it a little more information. So for a slider, for instance, you would want to give that slider a label UI elements attribute, which is an array that contains the UI elements for those three labels.
We need some way for that assistive app to know that difficult means one and easy means five. And so each of those labels needs to have a value attribute. So that, okay, I've picked this one and the value is five, so therefore that slider should be set to five. And we're going to hook one of these up as well. So, let's get to it.
All right, we're on stage three now. And we're going to do this entirely in Interface Builder. Well, we'll go into Xcode first. So launch the project for Dicey stage three. And then on the left-hand side, down in Resources, is the main menu .nib. I'd like you to double-click to launch Interface Builder. And I'm going to hide others.
Does everybody have the main menu open? Show of hands. How many folks need another minute? Or close? Okay. Alrighty. So, first let's do the button. Select the first button with the arrow on it. Go to Tools, Show Inspector. And in the Inspector, we're going to go to the Accessibility pane. It's also Command 6. To add a description, we just type it in. We're just going to call it "select".
And then we're also going to indicate that this button has a title, which is "ones" or that text field that reads "ones". So for this, this is very similar to hooking up an outlet in Interface Builder. We control drag from the button to "ones". And then in the Inspector, we select AXTitleUIElement and click Connect.
And then for a little practice, we just Ctrl+do the same for the two text fields on either side. Ctrl+click from the text field to its title, select AX_Title_UI_Element, and connect. And third time, Control-drag to the title, select title. You can also double-click here. But we'll hit Connect, hook those up. We'll save our changes.
Head back to Xcode and Build and Run. So, we'll roll the die, take a look in the Accessibility Inspector, first at that button, and you'll notice that Indeed, towards the bottom there, the AX description is in place. It's reporting itself. It is select. And in fact, it also has a title UI element, which is an AX static text. Let's freeze for just a moment. So again, Command-Fn-F7 to freeze on that button. Oops, lost it. And let's turn highlighting on so we can see that that button is highlighted.
And then go to its title UI element. And you'll notice it jumps over that it does indeed refer to ones. So now we have a good deal more information about what this button could possibly be for. And then let's unlock and quit. You can leave the Accessibility Inspector open. Let's close this project and go on to step four. So let's close that nib file as well. So step four, we're going to accessorize the slider.
Does everyone have Step 4 open? And in Step 4, let's take a brief look at the Nib file. And in the Nib file, the main menu Nib in step 4, if we select the app controller, And then look at the connections. I've added a few connections for the sake of time. I've added an outlet, I should say, to the slider in our Preferences pane and to each of the labels: Difficult, Easy, and Normal. So we can close Interface Builder. And in the AppController class, back in Xcode, dcappcontroller.h, in the header, let's uncomment these IB outlets.
And then head over to the .m file to the first commented-out method, SetupSliderAccessibility. And if we comment that out... Just take a look at the code. What we're doing is overriding some attributes via code. So the first thing we do is we just grab the min and the max value of the slider. In the first section, we're setting up that array of labels.
Now, one thing that's important to note is that it is the cell of a control that is typically accessorized, not the control itself. Because when we move that cell into things like a matrix or into a table view, we want that functionality of access - that accessibility functionality to travel with the cell, not the outer control.
So, even though we have an outlet to the actual control, we want to be using the cells within those controls as the elements that we're setting as the UI elements. So we make an array of the cells of those three labels, and then using accessibility set override value for attribute, we take the cell of the slider, and we set that array for the attribute, and its accessibility to the control. label UI elements attribute.
And then for each of those labels, we need to provide a value. And so all we're doing here is again setting an override value. The value itself, for the difficult label, it's the min value. For the normal label, we just take the middle value by calculating it. And then for the easy label, we just take the max value of the slider, and we set each of these on the appropriate label for NS Accessibility label value attribute. And then finally, let's uncomment and awake from Nib calling this method. And let's build and run.
And now if we bring up our Preferences and Accessibility Inspector, If we point to the slider itself, you'll notice that kind of midway down it has AX label UI elements, an array size of 3. And if we point to any of our labels, it now has an AX label value. This one's 5, 3, and 1. Is everybody there? Show of hands. Excellent. All right, and that's it for this particular phase, so we can quit out of all that. And back to the slides.
So it's really easy to customize instances. Adding descriptions, dragging up this thing as the label for this other thing in Interface Builder is extraordinarily easy. We had a lot of buttons that we didn't go through. It can be a bit time consuming, but not very difficult. And even if we need to do it in code, we just need to get access to them and set a few values here and there. So it should be very simple to improve the accessibility of your applications. So definitely encourage that. But we still have one glaring hole in our accessibility story for Dicey, and that's the fact that I still have no dice.
Which is kind of tough in a dice game. So what's the last bit that we need to do here? So we already saw that the view is reporting itself as a group, but it doesn't think that it has any children. So we're going to have to implement the children attribute to return a bunch of dice. We're going to have to hit test them so that when the mouse is over them, we say, "Oh, this is the guy you're looking for." Focus testing, because dicey, in fact, if I have full keyboard navigation turned on, I can tab through all of the dice.
I can hit the space bar to toggle the hold on it. So that's all I need to do on the dice view, but the DC die class is just a subclass of NSObject. So it doesn't implement any accessibility methods. So we're going to walk through the implementation of all ten of those methods. And then there's one or two places where we need to let an assistive app know that something has changed and ensure that some proper notifications are sent. So let's take a look at those. So we're in stage five.
[Transcript missing]
Okay. First, let's look at DC Dice View Accessibility. And we'll look at Accessibility Attribute Value. So we already dealt with the role earlier, but now we need to do something to return the fact that we have children. So, if the attribute that came in is the children attribute,
[Transcript missing]
So, Accessibility Hit Test.
We need to do hit testing. Now, by default, we saw it was working fine. We mouse in, it shows up. Okay, our hit testing works. We know that we have the mouse over us. But if something below us needs to report that it is the item that is under the mouse, we need to pass the message along.
So here, if we're not cleared, we'll just find the point, We'll find the die that goes to that point and we'll pass the message down that it needs to do an accessibility hit test. Now, when a point comes in, it is always in screen coordinates. So, first we need to convert from the screen to the window that we're in. So, convert screen to base in our window. And then we need to convert from the window down to our local view coordinates. So that's what we're doing in the second line.
And again, this is Tiger, so I'm not using a fancy Objective-C enumeration. But we can fix that for Leopard. Alright, now for focused UI element. In this case, the DC Dice View is keeping track of which die is the first responder. It holds the focus, but then its sub-elements kind of move along and have the keyboard focus.
If-- We are the first responder. Then we know which of our sub-dice is the first responder, and so we can pass along the message to it to return the focused UI element. Now, if you get the message "focused UI element," that means the application knows that the focus is at least within you.
It might be something beneath you, but it's at least in your purview or one of your children. Similarly, with the hit testing, if you get that message, you know the mouse is within you or one of your children or their children. Okay, let's move over to DC Die.
And here we need to implement all 10 methods of the Accessibility Protocol. But most of them are relatively short. So one of them you knock out is accessibility is ignored. You say, no, I'm not ignored. Accessibility Attribute Names. This is simply an array of strings that we return. We make it static so we don't have to keep making it and getting rid of it.
Now, we had decided that the best role for this, of the existing roles, was to accessorize this as a checkbox. Now, sometimes it's not so much the code that's the challenge when you're accessorizing, it's figuring out how should I expose this object. And in this case, because it works like a button in that I can press it and have it change, but it also has this state of being held and unheld, we decided to accessorize it as a checkbox. So where did I get this list of attributes? I just went to, I think, TextEdit Preferences, turned on Accessibility Inspector, moused over an existing checkbox, and just jotted down what list of things a checkbox returns so that I could accessorize myself like a checkbox.
This is simply a bunch of names of what we're going to return. So that's method two. We'll skip this one, it's a utility method. For the biggie in our whole thing here is Accessibility Attribute Value. This tends to be the biggest method when you're accessorizing. It's where you give back all the information.
So, let's take these one by one. First of all, we're saying we're a checkbox, so if they ask us our role, we'll say we're a checkbox. Not so bad. Otherwise, if they ask for the role description, there's another one of those handy convenience functions in NSAccessibility.h that we can hand in a role, and we can get back the system's role description localized correctly. so again, not so hard for us to do that either. If they ask for our parent, we are keeping track of our parent so we can return it.
Screen geometry - so our position and our size. So, again, we know our bounds. It's a property of the die. It knows its bounds. So for the position we get our local point, there's a bug in the is flipped code, which I discovered after I posted it to the WWC site, but we can correct that as a little exercise on all of our parts. The local point we convert to the window point, and then from there to the screen, and we return our position.
And then for the size, we know our bounds, we can grab our size, we convert it up to the window. And since really the window and the screen have the same transform with regards to scale, we don't need to transform to the size because it's going to remain the same.
We can return the size. It's important to note that we're dealing with an NSValue object that's wrapping the point and wrapping the size. All right, let's jump up. We need to know our window, but we know our parent already knows what window we're in, so we'll just return whatever our parent says is correct. And the same is true of our top-level UI element.
Now for our value, "has hold" is representing whether we're held or not. That's our - we're saying that's our checked state, that's our value. So if we have a hold, we'll return a 1, an NSNumber, otherwise it'll be a 0. We can't be disabled, so if they ask if we're enabled, the answer is yes.
We are maintaining our own Boolean as to whether we are focused so we know whether to display our own focus ring when we draw ourselves. So if we're asked if we have the focus, we can respond correctly. We have no help, but we do have a description. And there's a little bit of logic here, because we wanted to tell how many spots we were showing. So I factored that out into another method. And the key thing here is we're figuring out how many spots we have. We mentioned that word "die." But also that these are localized strings, because you want your description to be localized.
Okay, that's three down. Seven to go. So, are we settable? The only thing that can happen is we'll allow somebody to set our focus. So, if they say, "Are you focused? Is that settable?" We'll return, "Yes." Otherwise, "No." Since there is one attribute where we can be set, if that's what they're trying to set, and they're trying to set focus to true, there's a method in our parent where we can set ourselves as the focus die. It's important to note that it's impossible to set something to be unfocused. It's yes or nothing.
Actions. Since we are a checkbox, a checkbox has a press action. So we'll return an array with one object when somebody asks us for our action names. Again, we have an action description and a convenience function so that we can get the appropriate description for a given action. And then if we're asked to perform an action, we do the appropriate action in the application.
[Transcript missing]
If we get the hit test method, we know we are the thing that the mouse hits. It's within us or one of our children. Well, we don't have any children, so we would just return self. However, what if somebody subclassed our die and decided that the die each spot was going to be accessible? For whatever reason.
Well, and maybe some of them had children and they were ignored. We would want to make sure that when this was called by a subclass, that we actually returned an unignored ancestor. And so in that case, we would wrap it in this to make ourselves a little safer and return self. The same is true, there's nothing, there's no more granular focus than us. So therefore, we return self, or kind of that wrapped self, as the focused UI element. Okay. Bye.
[Transcript missing]
And now your homework is to go play that in VoiceOver. Because it should work. Alright, if we're done, if we could get back to slides, if not, that's fine too. So let's go quickly through. So the Accessibility Developer List is accessibility-dev. We'll have all this information. We'll have a lab tomorrow at 8 o'clock.
Excuse me, 9 o'clock. I won't be here at 8 o'clock. And immediately following this is the Accessibility and VoiceOver Feedback session, which is in Knob Hill. So please come to the lab with your applications. We can talk about accessorizing them. And please come to the feedback forum. Just in summary, more Mac OS X features than ever in Leopard are relying on application accessibility.
Now, we could have built Dicey with standard controls. I could have just made a group view that drew a background and put some buttons in there for the dice. That would have made my life a little bit easier. But it would have made a bad example of how to accessorize from scratch.
So if you are ever on the fence of, well, maybe I should just do my own or use the standard one, have accessibility stick in your mind as a reason to veer towards the standard one, because you get a lot more functionality. Or you could have, say, for zero work or maybe just a little bit of tweaking work.
Keep away from mouse-only operations when you're designing your apps. Adding those attributes like descriptions and title UI elements - you think it's very small because it's not a lot of work, but it's a tremendous help in accessorizing. And if you're doing custom views and controls, make them accessible. If you're subclass or tremendous viewer control, use the superclass, don't reinvent everything. But then in sub-elements, the 10 methods are terribly bad at all.