Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: wwdc2002-304
$eventId
ID of event: wwdc2002
$eventContentId
ID of session without event part: 304
$eventShortId
Shortened ID of event: wwdc02
$year
Year of session: 2002
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: ...

WWDC02 • Session 304

Cocoa Controls and Cocoa Accessibility

Cocoa • 1:08:03

In Cocoa, the term "control" refers to a wide range of user-interaction objects, from simple buttons and sliders to sophisticated elements such as tables and column browsers. This session presents the control classes in Cocoa and discusses how to use and extend them. Accessibility topics, such as making keyboard-navigable controls and dialogs and ensuring that your applications follow Apple's accessibility guidelines, are also covered.

Speakers: Chuck Pisula, Mike Engber, Kevin Aitken

Unlisted on Apple Developer site

Transcript

This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.

Good evening, folks. My name is Chuck Pisula, and I'm an engineer in the Cocoa Frameworks group. Today we're going to be talking about Cocoa Controls, and my colleague will be talking about the Cocoa's new accessibility APIs. Now, Cocoa has a large variety of controls, and you'll find that these controls are easy to use. They can be customized and can bend to suit your needs. And also, you'll find if you need to write your own control, well, that's also pretty easy to do.

So today, my goal is to talk about some of the basic control concepts and APIs, and my hope in doing so is that when you leave here today, you'll have a basic understanding on how to use Cocoa Controls and how to do some customization of these Cocoa Controls.

and I'm also hoping that by the time you leave here today, you'll have some insight on how you write your own control. Now, to underscore all these points, as we go along, I'll be presenting a demo which shows how you can write your own control, extend it, and use it in different various ways in your own application.

So many controls in the Cocoa frameworks are subclasses of these things called NSControl and NSL. And we're going to focus today on those specific set of controls. Now, there are also many other UI elements in the Cocoa frameworks which are subclasses of things other than NSControl and NSL. For those today, I'm just going to refer you to the documentation, and we'll move on today focusing on NSControl and NSL.

So I mentioned two classes. This doesn't mean, however, that we're going to be talking about two completely separate things today. In fact, NS Controls and NS Cells work very closely together, and in general this means that they have very similar APIs. Typically, an NS Control does its work, in fact, by asking its own cell or one of its cells to do the work for it.

Now, a control, being a subclass of NSView, is the one that knows its location in the view hierarchy, and that's sort of its job, is to manage either its only cell or one of its cells and tell it what to do, and not only what to do, but where it should do it. And we're going to sort of discover how this happens throughout our discussion.

Now, it turns out that this division of labor between control managing its cells and cells doing work for its control provides us a lot of flexibility. In fact, it makes it pretty easy for us to write a control such as a table view, which can group multiple, potentially even different kinds of cells. And it can do this in a manner that doesn't require lots of memory because a cell doesn't have to carry along its own graphics context like an NSView does.

So let's talk a little bit more about NSControl now. As I've said, NSControl is a subclass of NSView, so it's the one between NSControl and NSCell that has a place in the view hierarchy. And it has sort of two main functions. One is to manage its cell or multiple cells and manage when the cell does the work for it.

So a control will ask its cells to do things like drawing for it, store values for it, and so on. And the other bit of work that it typically does involves things not related to cells, sort of aggregate-level functionality. For instance, a table is the one that's in charge of maintaining a row selection. Cells do not do the row selection.

Now, I've been talking about this point that cells do the work for controls, and here is an example of how a cell can do the work for control, even though it doesn't have a place in the view hierarchy. For instance, when a cell is told to draw, it's told where to draw and in what view it's doing its drawing. Now, besides drawing, cells have lots of other things that they do. They are able to set and store values and typically provide functionality that allows you to customize the look of a control.

Now, I've been driving home this point that NS Control does its work by telling its NS cell, or one of its cells, to do the work for it. And what this means then is that if you're writing your own control and cell, you're not writing twice as much code.

Because typically, NS Control is simply, its methods are simply covers for NS cell methods. So when you ask a button for its string value, it just implements it by asking its button cell for its string value. And you'll find that you don't actually really even end up writing lots of this boilerplate code that forwards methods, because NS Control itself provides most of this base functionality for you, so that you don't have to do it yourself.

Now, so, from here on out, since controls and cells are so similar and have very similar APIs, I'm mostly going to be using the term "control" when I'm referring to NSControls and NSCells. And where I want to be specific, I'll specifically say, "This pertains to NSControls or this pertains to NSCells."

So just to simplify the discussion, I'll use "controls" to mean both for most of the time. From here, we're going to move on to talking a little bit about some of the standard functionality provided by controls, including setting and getting values, dealing with target actions, delegation, control sizes, and various other things that cells provide for you. Excuse me, controls.

So to start off, controls have this concept of setting values, and it's not really a very hard concept to understand. But the thing that's interesting to know is that typically a control implements lots of different The first thing I want to talk about is the value of a slider.

It seems natural that a slider would provide you a set floating point value method, and that would reflect in the slider the value that you just set. However, a slider also allows you to set its value using a string value. As long as it can convert that into something it knows how to display, it will go ahead and convert the string into a floating point value.

So it's good to know that whenever you're using controls, you can typically set any kind of value on them as long as they can convert it into what they need to do. And likewise, they can return you values in multiple formats. So you'll also want to keep this in mind when you're writing your own control, that if it makes sense, you should support as many different kinds of setters and getters that you can.

The next thing I'm going to talk about is control size. When you're dealing with views, normally you can have a view resized to just about any size. But when you're dealing with standard UI elements, typically you want to have them be one of the standard sizes in the system. And this control size property that controls have allow you to pick either a small or a regular variant of the standard controls in the frameworks.

So if you want to have, for example, a small button, you would set its control size to be in a small control size. and typically the cell is the one that implements this control size property. But when you have a control that doesn't have a cell, it makes sense for the NSControl to be the one that implements this.

So for instance, NSTabView doesn't deal in cells, so it's the one that implements this control size setter. Okay, so I figure everybody's already attended the intro sections, or they're already very familiar with target action, so we're not going to talk about the concept of target action. I'm just going to mention that controls are, you know, typically tend to be things that users interact with and they click on, and users expect things to happen when they click on them. And this is where target action comes into play.

Controls have a target that they send an action method to when some sort of user interaction has happened. When you click a button, the action method gets sent. When a slider value changes, the action method gets sent. And for some controls, they will actually do this continuously, or they could wait until you're done manipulating the value. So a slider could continuously send this action, or it could wait until you've reached your final value that you decided to pick.

An interesting thing to note when you're programming is that the sender argument in these target action methods will always be the control. If for some reason you need to access the cell, then, well, it's either the only cell that the control has, or if it has multiple cells, it'll be the selected cell. And sometimes controls provide their own actions, so I might want to be able to receive a distinct action method when someone double-clicks in a table view row.

Many people writing their own applications often want to customize or be involved in the text editing that's going on in a control, and controls allow you this ability. They give you a couple different options. So if you need to customize text editing, one way is to implement some delegate methods that let you know, for instance, that a text value just changed, or you could have the control ask you, "Should text begin editing?" and so forth. I'm going to touch on a little bit of this API, the specific API for this, a little bit later, so I'll just leave it at that for now.

If you need to customize how editing actually works, there's something called a field editor that you can modify attributes for or even replace. It turns out that Even if you have multiple text fields in a window, there's only one view that does the editing. When you start editing in a text field, a text view is swapped in its place, and that's where the editing actually happens. Typically, you don't have to worry about this level of indirection, but if you want to customize editing, it's something you might need to know about.

Now, you're probably maybe thinking, "Why would I need to customize this editor?" One good example is the secure text field in the Cocoa frameworks. It goes ahead and replaces the standard field editor with its own so that it knows that it shouldn't copy and paste. And finally, another way to customize text editing is to simply attach formatters to a text field. For instance, you could force the values in a text field to be date values.

Okay, so I just mentioned these text editing notifications and delegation, and now we're going to see what the API looks like. In general, controls will provide their own delegate messages so that you can customize their behavior. So each specific control will have their own specific set of these delegation and notification. But it turns out that any control that allows text editing has a standard set of delegate messages and notifications.

And basically what it does is send you... Let me back up a second. The control... The table view that's being edited wants to be the delegate of this field editor. It's actually the one that's receiving text-editing delegate messages, because a table view really needs to know that text started editing and it needs to know when it ends editing.

But being a good citizen, any control that is doing this decides that it will forward these delegate messages to you so that you can be part of the decisions that are going on. And you can also know that, for instance, text just changed in this table view that's being edited. So if you need to know, for instance, that text has changed, you would register for the NS Control text to change notification on your table.

Okay, so controlled, excuse me, NS cells are the ones that do lots of the work for their controls. And in fact, we've already touched on drawing, that cells, even though they're not a view, they do the drawing for their NS control. They also can do things like handle mouse events.

So you see the method up here, trackMouse, indirect of view, until mouseUp. This is the method that gets sent to a cell when it starts mouse tracking. And of course, again, you'll see this as a recurring theme that when we tell it to do something for a view that involves event handling or drawing, it's told what the cell frame is and in what view it's in.

It turns out that if you want to do mouse tracking at a finer granularity, there's a set of three methods that you could override that make it a little easier than doing this one. And I'll just refer you to the documentation for that. Now, it sounds like basically cell is always told what it's supposed to do.

It turns out it actually is allowed to tell its control some things too. For instance, if a cell knows that it needs to be at least so big, you can override the cell size for bounds method and try and tell your control that, "Hey, I need to be at least this big."

Okay, so you can draw in a cell using a cell. You can track the mouse using a cell. You can actually even provide context menus as a cell. Views provide the functionality of having context menus. There's methods like set menu, return the menu, provide a default menu, but cells are also allowed to provide their own menu.

And again, you see this mantra again, "Menu for event, in-rect of view." There's the in-rect of view parameter, so that the cell can use that information to determine, you know, say if I'm in the right half of the cell, then I provide this one menu. If I'm in the left half, I'll give this other one.

Okay, so that's a little bit about some of the basic concepts and APIs that you'll find when you're dealing with controls, NS Controls and NS Cells. Now, there are lots of simple controls in the Cocoa framework you can just use right off the pallet in IB. And because they're really so simple to use, I just decided I would throw up a picture of them and not really talk too much about them.

I think it's something everyone can go and explore on their own. But of the ones I have up here, there's some interesting comments to be made. The checkbox, the radio button, and all the variations of button you see on the left side, actually all happen to be just different styles of an NSButton.

The secure text field, you see that actually that just has the dots in it, is a subclass of NSTextFieldCell, and these are the two main forms of text entry. There are controls that allow you to just display an image. Controls such as the color wall that allow you to select a color and bring up the color panel. And there are controls that combine Characteristics of things like the text field and pop-up buttons and give you something like the combo box on the far right.

The combo box is part text field, part pop-up. So, if you want to find out more about those, I would just go on IB, drag them off the palette, and just play around with the different attributes that you can set and use with them. For now, I'd like to go to the demo machine.

Okay, for our first demo, we're going to start by creating our own control, and you'll see some of these methods that we talked about in action, as well as some of the controls that we just saw on the screen. So let's start out by going, well, let's start out by me explaining what we're going to do. We're going to start out and try and create a project that has our own clock control on it.

So we're going to create a subclass of NSControl that's a clock control. We're going to create a subclass of NSCell that's a clock cell. And we're going to use it just in a straightforward way in this demo, but then we're going to use it throughout in our other demos and extend it. So to start, let's show you the declaration of this clock cell.

Clock Cell actually is a subclass of NS Action Cell. That's something I'll just refer you to the documentation for. NS Action Cell is the one that has the action and a target. It's aptly named Action Cell. And we've declared a couple methods. We want to be able to set times.

We want to be able to connect it to things in IB that tell it, "Hey, take your hour value from me," and so forth. And again, because controls and cells do lots of the same things, you see the clock control has pretty much the same interface as the clock cell.

So let's go take a look at the code. And let me point out first, there's a lot of code there, but a lot of it's just legalese. The first thing I want to point out is that there's really not much to the NS Control subclass that we've made. There's a couple things we do for setup.

and then a couple of the setter methods and the take value from type methods. And all we really do is forward it off to our cell. We don't really do anything ourselves. And you might be thinking, "Well, how does this cell ever end up drawing? We don't tell it to draw anywhere." Well, it turns out that NS Control provides lots of standard default behavior for it that just does what we want. So we're going to be able to draw our clock, be able to track the mouse, pop up a context menu, all with what we currently have here, which is basically just default behavior. So let's go look at what we've done for cell a little bit.

And before I go on, let me point out that I'm not really going to hover or hang around with any of the code too much. I'm going to skim over it pretty quickly. But we've made the code available. It wasn't available as of 3 o'clock today, but it should be there soon. What you should do is log into the ADC site and use your seed key to check out what's new in the downloadable area.

So we've got some initial setup. We have some initialization routines. I'm going to skim over these. They're pretty standard, in it with coders, archiving-type things. We need the ability to be able to copy for various things. They're just boilerplate things that we sort of fill out always. And we're going to have a clock that's changing its value, so we're actually going to want to be able to send an action message to someone when our value changes. So here's a little utility routine that will send the action for us.

Moving on, we've got a bunch of utility routines that set the time in various manners by incrementing, by moving to a specific time, by checking the angle from north. We've got some other setters. And I decided that it made sense to allow someone to set it using a string value. As long as it's a format I understand, I'll convert the string value to a time.

We've implemented our "takeMinuteValueFrom," "takeHourValueFrom," "takeStyleMethods." Then we have a routine here which draws the hands of the clock. This is a lot of math. I'm just going to skip over it. And finally, we have our Draw With Frame, which does the work of looking up an image that we draw in our background and finally drawing the image and then drawing the clock hands.

We've got some utility routine here, I'll skip over. And then we have... If we scroll down to it, the mouse tracking routine, which is going to track the mouse. So when I click on the clock, I want it to update its time as I move the mouse around.

And the stuff I skipped over is just sort of hit detection and event tracking. That's stuff you can look at in the demo code if you download it. And then I've overridden menu for event to return a context menu, which will show me the time and allow me to change the time.

and that's it. So now let's go look at what we have in Interface Builder. I've got a lot of material to cover, so I've already... Excuse me. I've gone ahead and already dragged out all of the things that I want to use in the interface. I'm just going to connect them up so I can show you what's happening.

I want this slider value, when it's moved to...

[Transcript missing]

We're going to let our application controller have an outlet so that we'll call it a time readout. Whenever the clock time changes, we will actually have the clock send the action method to the app controller, and then it's going to update the readout. So we connect the clock's action to our app controller, and it'll invoke the clock time changed method. So let's save. We'll go back to Project Builder 2nd and show you the action method.

"It's really pretty simple. All it does is ask the clock for its string value, which the clock converts to a standard format, and we display it. So let's build and run." Okay, so you saw again that the control code was really pretty straightforward. We didn't tell it where it was supposed to draw, when it was supposed to draw, but it's drawing because it inherited that behavior. Same thing for tracking. Thank you for joining us. I'm going to go back to some more discussion and then we'll come back to this demo a little bit later and extend it and use it in different ways. So if we could have the slides again, please.

Okay, we're going to talk about some more controls now. Specifically, the stuff we've been talking about so far have been sort of single-celled beasts, where, for instance, a button has one cell, an NSButton cell. Now we're going to be talking about controls that typically have multiple cells. We're going to talk about NSTabView, we're going to talk about TableView, OutlineView, and Browser.

And as tab view actually is a class that doesn't really use any cells. And as tab view is a control because it has a target in action. So when you change the currently selected tab, it'll send an action method to its target. But the things that it displays happen to be the views of its tab view items. And the things that it displays in the tab at the top happen to be the label of the tab view items.

NSTabView in Jaguar now supports directional tabs, so you can have tabs in the left, right, bottom, and top. And also, it supports tabless tabs, if you happen to need something like that. It can be useful as a simple view swapper. And because NSTabView doesn't have any cells, it's the one that implements this control size property, so you can have small and regular controls, but you would tell the tab that you want the small control size.

Next, I'm going to talk about matrix. And the thing I want to point out about matrix is you usually don't make these programmatically. These are usually things that you get in IB. If you've played around in IB and happened to, I believe it's option drag, say a button, you'll notice that all of a sudden you start getting more buttons, all grouped in a regular grid. Well, this option dragging has created a matrix of NSButton cells for you. It started with a button, with one cell, and then converted it into a matrix of button cells.

Now, if you need to create a matrix programmatically, it can surely be done, and it's useful, for instance, when you want to have a gridded group of cells. And there's lots of API that allows you to control the arrangement of cells and control the appearance and so forth. And I'm not going to go into those because the real reason I wanted to talk about matrix is because it's used by NSBrowser.

So NSBrowser is used-- you're all, I'm sure, familiar with it from the open panel. It displays hierarchical data. and each column in the browser happens to be in NSMatrix that's wrapped in an NSScrollView. These matrices are filled with a bunch of cells, and all the cells happen to be NSBrowserCell or NSBrowserCell subclasses. And the data that's going to go into these cells is provided by you.

When you're writing your program, you're going to supply a delegate for the browser, and it's this delegate that will load data into these cells that the browser then displays for you. and again, there's lots of API that I would recommend just going to the documentation for to figure out how it allows you to control NS Browser in various ways.

Okay, so you're the one that's in control of loading the data into the browser. So how do you do it? Well, there's two mechanisms for doing it. One is referred to in the documentation as active. The other one is referred to as passive. I sometimes prefer to think of this as immediate or lazy. Not because I'm a lazy person, but just immediate or lazy.

In the immediate manner, when the browser needs to load a new column with some data, it's going to send a message to your delegate and say, "Hey, go ahead and load a list of this many browser cells into this matrix that I sent you." And it turns out that this matrix happens to be at a particular place in the browser. So you're given the matrix and you fill it with your own custom NSBrowser cells.

When you're loading data in a lazy fashion, and this makes sense, for instance, if you're an open panel, which doesn't want to necessarily touch everything in a directory that you just saw on a network file system. So what you would do in that situation is Implement methods that tell the browser, "Okay, for this new column that you just saw, this is how many rows I want to have in it." And then you don't actually load the data right away. You just tell it these are how many rows that the column has.

Now, each time the browser has to display one of those cells, it will realize that cell's not loaded, and it will send you the "browser will display cell at row" method. And at that point, what you do is load the data. So the first time the cell has to be displayed is the time when you load the data.

Now, because browser cells can be loaded lazily, browser cells have to be able to tell you if they've been loaded or not. And the only other real property that's interesting in NSBrowser cells themselves is the fact that they can be a leaf node or a non-leaf node. Typically, you're going to just make your own subclasses of NSBrowserCell, and that's what you're going to load into the matrices of your browser.

Okay, so let's move on to NS Table View, which is not a hierarchical data. It's basically a simple list that is divided into many different parts. A table happens to live inside of a scroll view, which has a header view, which is divided into multiple columns. So there's many parts to an NS table view. The important parts, which we're gonna touch on, are the table itself and the table column.

Okay, so just like NS Browser, NS TableView depends heavily on a data source. In fact, it depends even more heavily on someone to supply it with data. Whereas the browser has an immediate mode where it can load all the data up front, TableView has no such option. All of its data always comes from the data source.

And one of the main reasons for this is, for instance, in, say you're writing a database application, and you had thousands of elements that you wanted to have on a table, you would never want to load all those elements into a table. You want to load it in a lazy manner. And another good reason for this is that this clearly separates your model from your view in your application, and allows you much more flexibility.

So as a data source, you implement two methods. The first one, much like the browser, tells it how many rows it has. And the second one is a method which provides data that's going to be given to a cell so the cell knows what it should display. So the second method is the interesting one, which I'm going to talk about a little bit later. You notice it's returning this thing called, given a table view, it returns the object value for a particular intersection of column and row. And we'll talk about a little bit later what this object value is.

Now, since you're the one that's controlling all the data, there are a couple methods that you need to know about so that you can tell the table it needs to refresh itself. You can tell it to reload or note that the number of rows in your cell, the table has changed. So let's talk about this object value.

Remember, there are a bunch of different set methods that NSLs had implemented: setString, setFloat. Well, one that they all happen to implement also is setObjectValue. So the object value that's returned from this data source method is handed to a cell in the setObjectValue, and the cell looks at it and says, "Oh, the object value I was handed was this type of thing. I'll convert it into what I need to, so I now know how to display." And a cell also, of course, then needs to be able to report what its object value is. So it has a setObjectValue and a getObjectValue type method.

An example usage of this would be, say you had a text field in your table. Well, your data source could return the string created by string with format, Homer, and it would set the object value of that cell to Homer, which is then going to be displayed at that column and row intersection.

Okay, now say you also want to be able to edit your cells in your table. There's another method you would implement in your data source, which is similar to the getStyle object value, except for this one is a set object value. So when an object value changes, you'll be sent the tableView set object value for table column row message.

The object value you're set is the new object value of the cell, and it's up to you to store this in your data store, so that the next time it asks you for the object value, you return that new object value. Okay. And finally, there's all sorts of other data source and delegate methods that you can implement for your table view that allow you to do things like drag and drop and so forth. And I'll just refer you to the documentation to find out more about those.

TableView also provides lots of delegation and notifications that allow you to customize its behavior, such as you can know when a row was selected, You can know when the table header column has been dragged from one position to another. There's lots of delegation notifications. Table view is highly customizable. And that's my whole point in listing these up here. I'll just again refer you to the documentation to find out those. Just know that table view is highly customizable.

NSOutlineView happens to be a subclass of NSTableView, and it is a hierarchical version that can display hierarchies of data. And its main difference comes from the fact that it is able to display hierarchies of data. So it makes sense that TableView deals in rows, but it doesn't necessarily make sense that an OutlineView deals in rows, right?

Because, say you expand a particular row and display the children in that row, now all of a sudden all the rows have shifted around. So OutlineView doesn't like to deal in rows. Instead, it deals in these things we call "items." and you'll see this in all of the API for Outline View.

So again, Outline View requires a data source to supply it with data, and the difference here relates to the fact that it displays a hierarchy of data and it deals in items. and so you need to be able to tell it for a particular item how many children it has.

If a particular item is expandable, you need to be able to tell it You need to be able to tell it what the particular object value of a child is and so forth. And one important note is that the nil item refers to the root item of your outline view. So now I'm going to go back to the demo machine, please. And we're going to see a little bit how you can use the custom control that we just made in a table view.

So I've gone ahead and, ahead of time again, created a window with a table in it. It has a remove button and an add button. We're going to sort of make a mini-planner. And the columns of this table I guess I forgot to mention one of my slides. Columns are identified by something called an identifier, so that's something that we have to set in the attributes. We'll call this column "time."

and we'll call this column "info." So one will display the clock, the other one will have info. Now we need to wire some things up. We've created some new methods in our application controller to add an appointment, remove, and our controller needs to know about the table. And finally, the data source. The table has to know who it's getting its data from, so we'll set that up.

So now we should have all our pieces set up in Interface Builder. Look at the code that we've added. Remember, our application controller is the data source for the table. So we must have added some new methods to it. Well, first of all, it looks like in our Wake From Nib, we've gone ahead and changed the data cell of the time column to be a clock cell. And we've done some nifty stuff with the data cell in the info column. And we've started out with an initial appointment.

Okay, and then we've implemented our methods to add appointments, they update a datastore that we have, remove an appointment that will also update our datastore. And finally, the interesting thing, which is the

[Transcript missing]

We will access the record at the index in our appointment datastore, and then we will simply return the time of that. So the time will be handed off to the clock cell that's about to display, and then the clock cell will know, "Oh, this is my time. This is the time that I display."

And since we want to allow editing, we need to override the set object value data source method. So we receive a new object value, and that object value is passed to, is used to set the time, if we're in the time column, or set the information string if we're in the information column. I did mention that we're now using object values, so we must have to go back to our clock cell and implement the set object value method.

and I don't remember where it is, so we'll do a quick scan. And there's our new set object value method. What we'll do is look at the object value and see if it's an NS Calendar date. Well, we know how to handle that natively, so we'll just set the time using that object that we got. If it happens to be a string, well, we already know how to convert from strings to time, so we'll use that. Otherwise, we'll just raise an exception because we don't know how to handle it. So we go ahead, build and run.

And our initial point where we gave it is there. If we edit one of the table columns and say something like, "Release the hounds,"

[Transcript missing]

Okay, and that's all I wanted to show for that. We can add all sorts of records if we want. Okay, can we back the slides, please?

So now I'd like to move on to accessibility considerations, including keyboard accessibility, keyboard navigation, and Cocoa's new accessibility APIs. So the first thing in dealing with keyboard accessibility and keyboard navigation is having a keyboard loop. So first we need to set up a keyboard loop in our application's windows. We need to be able to display focus if we're running our own custom control. We also need to be able to tell the system we want focus, and once we have focus, we should do something with it. So we need to handle keyboard events.

To tell the system you want focus, override the AcceptFirstResponder method and return "Yes" if you want to be able to have focus. FirstResponder roughly equates to being the focused control. And now that we've told the system that we want focus, we need to tell it when and in what situation we want focus. If we want to act more like a text field and allow clicks to give us focus, then you should return "Yes" from "NeedsPanel" to become key.

If, however, you only want to receive focus when someone tabs to you when any control is accessible, then you should return "No" from this. So in general, things like buttons don't get focus when you click on them. They shouldn't shift focus away from a text field if you click on a button.

Okay, so we told the system we want focus. Now we have to know that we have focus. To know you have focus, override the become first responder method. To know that you no longer have focus, override the resign first responder method. And to check if you have focus, we said that roughly being first responder means you have focus.

Check with the window to see who's first responder. If it's you, you have focus. Now when you draw focus, you shouldn't draw focus only if you're the first responder. You should make sure that you're also in the key window. Because if you're not in the key window and you're drawing focus, we could have a situation where two windows are showing focus.

Okay, now that we know we've gotten focused, we need to be able to tell the system to redraw ourselves so that once we get focused, we update our drawing. So if you override become and resign first responder, you would do a set needs display so that you redraw yourself. And similarly, you would want to find out when the window you're in has changed its key state. And when the window changes key state, you again need to redisplay yourself to either show or stop showing focus.

and finally, if you're drawing focus and you need to redisplay, you should use the new Set Keyboard Focus Ring Needs Display Interact because keyboard focus rings can happen to fall outside of your view's bounds, and this special method will help take care of that situation. Okay, now that we have focus, we need to do something with it.

One way is to override key down and handle the raw key events. Another way is to override one of a series of standard methods that the Cocoa text input system will automatically convert Keystrokes To and Send To the focused control. For instance, when a right arrow is pressed on the keyboard, the text input system will convert that into a move right method and try and send it to the control that has focus. If it implements it, well then it can do what it wants to and move right. And if it doesn't, well then you'll eventually get a key down and you can handle it there.

Okay, so now let's talk a little bit about how you set up keyboard loops in your application window. Typically what you do is set the initial first responder of your window, and then for each control in your window, you would set the next key view outlet until you have the keyboard loop you want in your window. Now if you find that this is tedious, what you could do is just not set the initial first responder or do anything at all and let Cocoa compute it for you.

And it turns out, if you're not adding and removing views all the time, this is usually just what you want, so see if that works for you first. Now if you are adding and removing views all the time, you'll need to make sure that you maintain the keyboard loop, or you'll get in strange situations where a view in the keyboard loop is not actually still in the view hierarchy. Okay, and finally, I'd like to show a demo. Back to the demo machine, please. For this demo, we will add a focus ring to our clock.

I haven't shown anything yet. Okay, so we've added some code to our controller to do the focus ring stuff. We've told it, yes, we do indeed want to be first responder, and we'd prefer to act more like a button. We want people to have to tab to us to be able to get focus. We've overridden the view to move to Windows so that we can figure out when the window we're in has changed its key state. and we've overridden "become" and "resign first responder" to know that we have gained first responder and we re-display in those situations.

[Transcript missing]

We've added it to the cell. So when the cell receives a move right, it will increment the time. When it receives a move left, it will decrement, and various things for other types of standard messages. So let's run, and I'll just demonstrate what it's capable of.

So I tab and I notice I can't get there, so I forgot to do one thing. I have to bring up System Preferences and tell it that I want to be able to access any control. Because I returned "no" from that "needs panel to become key" method, that means I can only get focus there until I tab to it. So I've tabbed to it now. I can use the keyboard to change the time.

[Transcript missing]

and all I'm doing is using the keyboard. Okay. Can we go back to the slides, please? Okay, now I would like to invite up on stage the guy that will be keeping you from getting to the beer bash. And I hope I've left him with enough time so that he can get done and we have time for questions. Mike Engber.

My name is Mike Engber, and for the next 20 minutes or so, or until you get too anxious to get to the beer bash, I'm going to talk to you about the new accessibility APIs and what they mean to you as Cocoa developers. So in Jaguar, we've introduced some new accessibility APIs.

And basically what these APIs let assistive apps do is look at the user interface of other applications. For instance, a screen reader could use these APIs to discover the windows in another application, and then find in the window a button, discover what text the button displays, present that to the user, and then finally press that button.

[Transcript missing]

In this session, we're going to talk about the other side of those APIs, what the target application has to do in order to present its user interface. And specifically, we're going to talk about what Cocoa applications have to do. To help motivate you to take the trouble to make your apps accessible, Kevin Aiitken from the Speech Group is going to show what they can do with an app that is accessible.

We need to go to demo machine number one. Oh right, we're there. Okay. So the speech group's been working on a number of new features for Jaguar. Two of them are using the new accessibility API and I want to go through those real quick and I also want to touch on as a developer who's creating a custom control in Cocoa, I want you to think about these new built-in speech features and make sure that your custom control works well with those and I'll just touch on those real quick. So let me go to the speech preference panel.

And first what I'll do is I will turn on new feature in our text to speech system. Speaker 2: Speak text under the mouse. That speaks to text under the mouse as I move around the screen. Speaker 2: Speech recognition. I'm using accessibility API to ask it first what is underneath the mouse. It gives me an object and then from that object I get its title.

Well, that works really well for all these standard controls. But because I know that which ones I want to get the title from, which ones I want to get the value. But for custom control I'm not quite sure what I should get. Speaker 2: Cloud control. Speaker 3: And so in here we have some standard controls. Should be speaking.

"And..." "There we go." "Page..." "But for a custom control like the clock, we're not sure what we should be asking for. Should we be asking for the title? Should we be asking for the value? So what we're suggesting is that you add an additional attribute that Mike will talk about here in a little bit, about attributes.

But we would like you to add an attribute called 'Apple Synthesis' and that way we can look for that specific attribute and then speak it. So in this case, we've implemented that attribute." "At the tone, the time will be 3:12 PM. Beep." "So we've done something a bit."

humorist there, but that points out that you'll be able to fine tune exactly what you want our built in text to speech system to say when the user mouses over that. So, likewise, really quickly show speech recognition. And let me turn off the screen reader. So speech recognition has the same issue. What we do is we use the accessibility API to go through the front window and create a command for each one of the items in there as well as items in the menu bar. But again, we have the same problem for a custom control.

So you'll see a bit later that this clock, well, actually saw in Chuck's demo that you can increment and decrement. So something we'll be documenting is how you can provide the spoken command and map that to the actions that you support for the speech recognition. So that's a little bit of a demo. So, I'm going to go ahead and show you how to do that. So, I'm going to go ahead and show you how to do that custom control. So, with that, I'll turn it back over to Mike. Okay. Can we get the slides back?

Okay, so first I'll give you some good news. All the standard user interface elements that you use in Cocoa are already accessible. So if you use those, there's not much you need to do.

[Transcript missing]

Before we can get into the details of the protocol, there's a couple fundamental concepts I want to explain to you about the accessibility APIs.

One of the big issues that has to be solved is how does a target app represent its user interface to the assistive application? And this diagram shows an assistive application trying to query what's in a Cocoa app and a Carbon app. And the question marks indicate that it's not really clear how they should communicate their user interfaces back. Each of these applications has a natural representation of its user interface in terms of its own native types.

The Cocoa app has NSWindows and NSControls, and the Carbon app has WindowRefs and ControlRefs. There are some problems trying to use these natural representations. For one thing, it means the assistive app gets to do twice as much work. It has to understand all the native widgetry of Cocoa and the native widgetry of Carbon. Another problem is there is a lot of excess of detail that would be exposed.

If you consider a simple example of just a button in a window, in a Cocoa app that's really a button cell inside a button control, inside the content view of the window, inside another view, and finally you get to the window. The assistive app really only wants to know there's a window and there's a button in it, and it doesn't care about the rest.

So this slide is basically the same diagram with some boxes showing the new code that was added in Jaguar. The box in front of the assistive app are the accessibility APIs. The box in front of the Cocoa app is the accessibility protocol. And you'll notice that the question mark has been replaced with the word "UI element." A UI element is short for user interface element, and that's basically our solution to the representation problem. Everything is a user interface element. A button is a user interface element. A menu is a user interface element. A window is a user interface element. And there's even a special user interface element to represent the top-level application.

So basically, applications are represented as a hierarchy of user interface elements. User interface elements have attributes and actions. Attributes are how you get information about a user interface element. For instance, its title, or its value, or its children, which would be the basis for traversing the whole hierarchy. They also could support actions. A button user interface element would support press. A slider might support increment and decrement.

So the magic that makes this work is the accessibility protocol. And it's a protocol on NSObject because there's a wide variety of different objects in Cocoa that can act as user interface elements. For instance, windows, controls, views, and cells. And really, the only common ancestor in their inheritance hierarchy is NSObject.

So the first part of the protocol deals with methods. And so there's a method for returning a list of all the attributes you support. There's a method for returning the value of a particular attribute. There's a method for testing if a particular attribute can be set. And finally, there's a method for setting its value.

You're probably most likely to override the method for returning an attribute's value. If you consider override, you're interested in returning a custom value for the title of some UI element, you would override the accessibility attribute value method. And the first thing you would do is test if the name of the attribute that's being asked about is the title. If it is, you can return whatever you want for the title.

If it's not, you call super so that other attributes will be handled for you from the inheritance hierarchy. One thing I'd like to point out is that you should avoid inventing new kinds of attributes. Assistive apps aren't going to be expecting them and they may completely ignore them. So when I accessorize the clock, instead of creating a time attribute, I just use the value attribute and return the time as a string.

The second suite of methods in the protocol deal with actions, and there's a method to return the names of all the actions you support. and the rest of the team will be joined in the next session. You're less likely to override the action methods. If you create a kind of button, the odds are that you want it to support the press action, which you're going to inherit for free, and what you'd like it to do when it's pressed is invoke the action on its target. You should think of actions as very simple things, basically what you could accomplish with a single click of the mouse, pressing a button, picking a menu item, things like that.

and some other things you should keep in mind is that attributes aren't the only way that an assistive app can interact with your application. So you might say, "How am I going to implement an action that selects text because actions don't get any parameters?" Well, the way that's done is there's an attribute, the selected text range, and if the assistive app wants to change the range of text that's selected, they just set the value of the attribute.

So now that we know some of the basic concepts of the accessibility, we're going to bring Kevin back, and he's going to demonstrate a tool that explores how some Cocoa apps are exposed to the accessibility APIs. Great, thanks Mike. Let me turn speech recognition off just so we can have some more screen real estate here. So what I'm going to do is start up an application that we wrote called UI Element Inspector. Let me move it here and make the font larger.

[Transcript missing]

Let's go through the things in here. As you see right here, it says "analog clock." That is basically the type of object that we're getting back for. It's inside a window with the name "window," and it's inside an application named "clock control." As Mike talked about the attributes, it lists out all the attributes: the role, role description, some state information, whether it's enabled, focused, some references to its parent, its window, its size, its position, its value. You can see here's the string that's returned. And then here is this extra attribute that we used in the speech synthesis, giving what we call an embedded command string that produces that text-to-speech that we heard when we moused over it.

And then if we scroll down here a little bit, we'll see the actions, and it supports an increment action and a decrement action. So let's play with it just a little bit. And send it an increment action. As we do that through the accessibility API, you'll see the increments, and we can change and send it an action, or a decrement action.

And then we can also change attributes. So the ones with a W say that we can write to them. So we can do something like this. We can set one to be true. As we click set value, it will miniaturize the window and set it back to zero. And it will bring it back. So anyway, that's a quick demo of that. Our speech session is tomorrow afternoon in the same room. So if you'd like to attend that, we'd love to have you there. So thanks a lot.

[Transcript missing]

There's a method called "accessibilityIsIgnored" in the protocol, and if you return "yes" from that, then that means you're supposed to be ignored. So when the button returns its parent, if the parent's ignored, it should return its parent, and so on. Similarly, when you ask the window for its children, if the content view is ignored, we then go to the content view's children.

To keep you from having to write this code to walk these hierarchies up and down, we have a few convenience routines. If you're returning your parent attribute, you can use the accessibility unignored ancestor routine, and there's another routine if you're returning your children, and there's a couple other related routines for similar needs.

Another thing that the Accessibility APIs allow is to determine what user interface element is under the mouse, and we saw Kevin making extensive use of that. And there's also a similar facility for finding out what user interface element has the focus. So how is this going to be accomplished?

You can imagine, in a Cocoa app, the first thing we would do is find out what window the mouse is in, and then we could use View's hit test method to find out which view was hit. But that's about all Cocoa could do. And similarly, if we want to find the focus, we can find out what the key window is, and then we can discover the first responder. But that's about all that we can do for you.

If you happen to have substructure, like a matrix has cells within it, and you want to make it appear that one of the cells has the focus, that's really up to the matrix to do it. If we were trying to accessorize the analog clock a little differently, if we wanted the hands to show up and be hit testable, we would have to do that work.

So basically, if you have substructure that you want to expose, these methods that I'm going to show you here are what you're going to need to use. So there's one for hit testing and one for focus testing. And when these methods are called, it means it's already been determined that you have the keyboard focus or that the mouse is within your bounds. And that's your chance to decide if some subpart of you has it and return that, or failing that, you can just return yourself.

The last part of the accessibility APIs has to do with notifications. So assistive apps want to find out about changes in their target application. They want to find out when the application gets activated or deactivated. And then there's window events, if a window is miniaturized or deminiaturized, if the focus changes, or when UI elements get destroyed. So there's a whole variety of notifications, and most of them are handled by Cocoa for you.

If you need to post a notification, the most likely one that you would need to do is value change. So the clock would be a prime example. When we change its value, we'd want to post a value change notification. And the call you make is an S-accessibility post notification. That's fairly straightforward.

So there's one special attribute I haven't talked about that much, and that's the role attribute. This attribute identifies what kind of user interface element you're dealing with. For instance, there's like a role for buttons, check boxes have a role, menus have a role. A role should indicate that anything of that role should have the same set of actions and attributes, and assistive apps are going to generally rely on that information. So you should avoid inventing new roles. Assistive apps won't be prepared to deal with it.

Now, one pitfall you want to avoid is the temptation to invent a rule just to provide some instance-specific information. For instance, you've got a print button in your application, and you might ask yourself, "Well, how is this assistive app going to know that this is a print button?" The answer is it's not going to know.

The assistive app is only designed to help the user use your application. It's going to know there's a button there, it's going to know there's a press action, and it can tell the user that the title of the button is print. And that's where it stops. If the user can understand that and if they want to print, they can tell the assistive app, "Go ahead and perform the button action."

So in summary, using Cocoa is going to give you a leg up on making your app accessible. So I would say most Cocoa apps already work to a large extent with accessibility. Your custom UI elements, as long as you remain in the same spirit of what you're inheriting from, they're probably going to work too. But if you create something totally novel, like a clock widget, then you need to read up on the accessibility protocol and implement a few methods.