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: wwdc2007-125
$eventId
ID of event: wwdc2007
$eventContentId
ID of session without event part: 125
$eventShortId
Shortened ID of event: wwdc07
$year
Year of session: 2007
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: ...

WWDC07 • Session 125

Building a Custom Control for Your Cocoa Application

Mac OS X Essentials • 53:17

Walk through the design and implementation of a custom user-interface control with Cocoa engineers. First, learn how your custom control should support user input and keyboard focus. Then incorporate accessibility, support for resolution independence, bindings, and other features to make your control a first-class citizen on Mac OS X. Bring your laptop.

Speaker: Peter Ammon

Unlisted on Apple Developer site

Transcript

This transcript has potential transcription errors. We are working on an improved version.

My name is Peter Ammon. I'm an engineer on the Cocoa Frameworks team. This is Building a Custom Control for your Cocoa Application and that's exactly what we're going to do. All right. So this isn't something you have to do often right? Because Cocoa gives you a lot of flexibility in terms of modifying existing controls. You can take any NS button and you can override draw rec in a sub class and you can draw whatever crazy thing you want.

But we're not going to talk about that. What we're going to talk about the whole enchilada. We're going to make a track ball type control. It will be used for rotating objects in like a 3D context and =in our sample code, which is attached to the session, we have a panorama viewer and the track ball that is moved through the panoramic view in a very nice way. So I'm going to give you a demo of that now if I can.

So this here is the track ball viewer and much of what you see is drawn with quartz composer, not Cocoa and we'll talk about why that is and you can see, you can move through it nicely with the mouse, rotating up and down, left and right, can do a barrel roll by going around.

Of course it responds with the scroll ball, you can use the mighty mouse, that works well because it's got 360 degrees. We have arrow keys, so by pressing the up and down arrow keys, left and right you can rotate around and by holding down option, we can rotate in a circle.

And there's three text fields at the bottom and we can tab to those, we can click on one and it'll animate to the new position. Of course, it does not accept invalid input, so I type in pizza, it's not going to let me enter that. And there's more to this that isn't immediately obvious. For example, this control is fully accessible, it's exposed to accessibility. So let's talk about some of the decisions we made in designing this control and how we did it. Let's go back to the slides.

So we chose to sub class NS control. Why NS control? Well we could have sub classed NS view but, NS control gives us a lot more for free. For example, it gives us mouse tracking and it gives us more event handling, it gives us target action. So here are the topics we're going to cover. We'll talk about making a custom cell because, you can have a custom control without a custom cell.

We'll go through different appearances you have for your control, different states, different ways it needs to draw, choices you have for behavior of your control, should it send a continuous action, how we did the text editing component. We'll talk about making it accessible. We'll touch on bindings and we'll finish up with some common pitfalls you might run into when designing your own control and we'll wrap up.

So a custom cell. All right, seems like every control has a custom cell. There's NS date picker, there's NS date picker cell, there's button and then there's NS button cell, there's pop up button and pop up button cell, there's text field and text field cell, there's slider, slider cell. So are we going to need to make a track ball and a track ball cell? Well you think so, but really no, you do not have to.

You do not make a custom cell. In the Cocoa frameworks, we have to make cells for every control so that the control can be used in a table view. But if you're not planning to put track ball in a table view and we certainly aren't, then there's no reason to make a custom cell. We can do everything we need in the context of just the control.

And this gives us some advantages. It means we can add sub views to our control to help us draw for example. But that's not to say we're not going to use cells, in fact, we're going to use cells quite a lot and you want the control to have a cell and by have a cell, I mean the cell method should return something meaningful for your control.

The reason for that is that many NS control methods just pass on the parameter to NS cell. For example, set target and set action, these don't do anything unless you have an NS cell attached to your control. But it can be a plain vanilla NS action cell. This turns out to be the most base cell which implements what we need and this is the cell for our track ball.

So do make sure your control has a cell in your control class, you'll override the class method, cell class and return whatever class of the cell you want. Another way to do this is to call set cell class on the control. It's really up to you, there's not a whole lot of difference. All right, so custom cell, that's gone. We're not going to do that. Let's talk about the different ways your control can draw.

So there's a lot of different techniques for drawing in Cocoa and we've seen, I'm sure you're familiar with a lot of them. We can use a sub view of your control to draw and this is one of the things we can do because we're not making a custom cell.

You can also draw directly with cells, called draw with frame. You can draw with NS images, you can use Cocoa drawing primitives, bezier path and fillrect. Of course there's OpenGL and if you're interested in this, we're not going to talk a whole lot more about it, but I encourage you to attend the Cocoa drawing techniques session, which is just after this session and of course, come see us in the Cocoa labs.

How are we drawing track ball? Well, as I said, we're using quartz composer to draw the actual ball and all we do is make a sphere and we slap a texture on it and we put it inside a lighting environment and then we get a QC view and we add the QC view as a sub view of our control and we're done. That's how we can draw the entire ball with in one line of code.

Which is very nice. The labels are going to be draw with NS cell. This is not a text field cell, it's just plain NS cell and each angle as a number, is drawn with one cell and each label is drawn with a different cell. And while there's three text fields down there, apparently we're only using one cell and we're using it to draw three different places.

Now, because we're adding the QC view as a sub view of our control, we have this concern about the QC view intercepting events. So if you click on our control, but you actually click on the track ball QC view, you have a problem. Because the QC view will handle the event.

So how are we going to fix this? Well I'm sure you know how. You override the hit test method, that's what we'll do. So in hit test is called in our control, we'll call through to super and if the result is our QC view, then we'll return self instead of the QC view. And in this way, we will block the QC view from intercepting events.

Let's see we've got. Ah, so this is a cell and it's happy, because cells are, they're friendly. I think a lot of people have a certain fear of cells for various reasons and if there's one thing I could impart to you from this session, it would be that cells are not hard to use and do a lot for you.

Even plain NS cell by itself, not even a sub class, can do quite a bit and we'll use it. For example, NS cell can draw text and images and with Leopard, we have a lot of effects available for drawing those texts and images and some of those effects are visible in the track ball.

Cells are also used for text editing. Right, we clicked on the text field and we began editing in an NS text view and cell facilitated that. We saw how we blocked invalid input, we can do that with the formatter of the cell and also localization. So we saw that the angle had a period because that's the decimal separator used in our current localization, but in a different localization, you might use a comma.

The cell will handle that for us with the help of formatter. And cells are used for a lot of behavior. For example, target action, mouse tracking and some others. So when we draw the cells at the bottom in our track ball, as I said they're ordinary NS cells and we're giving them a number formatter.

We're also giving them a background style. Now what is a background style? This is something new in Leopard and all it is is just an enum. It's just an integer in C and the background styles visually describes the surface onto which you're drawing. What do I mean be visually describes? Well you'll never see a window background style or a menu background style, because those don't give you information about what it looks like.

But you might see a raised metal background style or plastic or glossy or something which gives you an indication of how the content you draw on top of the background style should look. And NS cell uses background styles to determine the text and image effects it applies when drawing on a different background.

And this isn't magic right, it can't look at the actual background. It's up to you as a client programmer, to tell the control what it's background is. So some cells, for example ordinary NS button cell, that draws a standard aqua frame, they have their own style. They draw their own background. So for example, this is an aqua button and any kind of background you put it on, it's going to be legible, it's going to look okay and the content, the text in the center, does not change it's appearance, even though the background of the cell might change.

So the cell itself has a background style inside it. We call it the interior background style and we can call the interior background style to get this and you can use this to determine how to draw when you're, if you were to draw on top of the cell, which we're not going to do.

Now for unbeveled cells, cells that just draw the text or images without any sort of border, they have to be told their background style. So for example, we're drawing one button on top of four different backgrounds, well the text becomes progressively less legible or it just kind of looks flat or plain, looks like it's missing something. In this case, the background styles correspond to light rays, lowered and dark. So that's NS raised background style, NS dark background style.

And by calling set background style on the cell, the text will apply effect so that it becomes legible. In the case of the ray's background style, it'll have an engraved effect. In the case of dark, it'll just invert it so it's white. And traditionally, you might have done this by using, giving it an attributed string, so you might have a foreground color in the attributed string to make it white. Background styles are a better way to do this because they will apply the right effects, even though we might change the effects. It'll make your application consistent with any changes we make in the future.

So how does track ball use background styles? Well as you saw, it draws these cells that don't really have a bevel, they had a slight bevel, but not really enough. So our track ball needs to be told the background style it's drawing on and to do that, we're just going to define. You can see this in the sample code as set background style method and all that does is turn around and call it on the cell.

So now for each of our images, I just sort of looked at the image and said, this image looks dark, this image looks light and I set the background style accordingly. As you can see in these screen shots here, the text fields draw differently depending on whether the image is dark or light and it makes, it makes it look nice or it makes it more legible.

All right, that's all I'm going to say about background styles Let's talk about the different appearances your control will have. The different states it can be in. And controls of course, draw these different states depending on the context and the point is to convey to the user what will happen if the user releases the mouse button or types a character or clicks on it. And there's a lot of different appearances.

Maybe more than you realize that are built into the standard control, so here they are. Here's some of them. I've divided them into positive appearances which convey something active, it conveys something ready about the control, it's going to respond. And negative appearances which convey something, the control's inactive, the control's disabled, it's not ready to be used right now.

So how do you draw these different appearances and when do you know which appearance to use? Well let's go through them. First appearance is active and active is what we call sort of the default look. In user controls and in front window, you can interact with it, it's not disabled. This is the look you use when no other look applies.

And the next state is pressed. And pressed means that it typically darkens the cell or the control and it means that it is tracking the mass. So for example, like I mentioned, all controls track until you let go of the mouse and this may not be immediately obvious so if you click on a button, hold the mouse down and move the cursor outside of the button, the button stops it from drawing press, but it's still tracking the mouse and the way you know that is because you can't interact with any other control in the window.

So tracking is modal in that sense and all controls track until you let go of the mouse. Some controls, when they're tracking the mouse, only draw press when the mouse is inside, like the button and some draw press even though the mouse leaves the bounds of the control. For example a slider.

What does this mean? Well this is really answering the question, can the action be triggered from outside the control. So with a slider, if you click and press on the slider, move the mouse outside, you can still manipulate the slider, but with the button, you click and hold on the button, you move the mouse outside, you let go of the button, it is not going to activate.

All right. So buttons, the answer is no the action cannot be triggered from outside. Sliders, the answer is yes. So you should ask yourself this question. Can the action be triggered outside the control for your control? And for track ball we're going to say yes. The reason for that is because it feels more natural to be able to move the mouse outside and then rotate it around to do a barrel roll or other types of maneuvers.

So how do we get this behavior? How do we indicate to the control, whether we want it to continue tracking or to continue drawing press? Well NS control will track the mouse for you. How does it track the mouse? Well inside NS control's mouse down event, it's going to ask the class of it's cell, another reason why we have to have a cell, we want to have a cell, whether it prefers tracking until mouse up and this is, this method really means do you want to continue drawing press, even though the mouse has left the bounds of your control. It will pass it's value to the track mouse method of the cell.

The cell, when it receives it, so in order to get this behavior, to continue tracking, you have to return yes from prefers tracking until mouse up and you act like NS slider. But the default is no in NS control, yet most of our controls actually override it to return yes. No that's not right, it's something else.

So this is the only case where we have to actually sub class NS cell to get this behavior. We don't do this in track ball. We use a different mechanism, but this is how you would do it if you were to use a cell for most of your behavior. You would override, prefers tracking until mouse up in a sub class of NS cell and then you would make that class the cell of your control.

So you made this decision, how do you know whether you should actually draw press, whether you're tracking the mouse? That's easy. You ask the cell if it's highlighted and if it is highlighted, you know to draw pressed. Highlighted is a term which means pressed in the context of NS cell.

Another mechanism to do this is to just sort of do it yourself inside mouse drag. So track ball doesn't have a separate pressed appearance, but it can track the mouse inside mouse dragged, which is of course, an event handler on NS control and the way you would accomplish this is you would get the location of the event in window coordinates, convert it to local coordinates, convert it to local coordinates, the coordinates base of your control and determine if the mouse is inside the region you're tracking by calling NS point in recs. So this is another mechanism to do mouse tracking and it's the one the track ball uses.

All right, another state that you might have is on or on in pressed for example, a check box. When you click the check box, it draws a check mark and that's it's on state. If you click the mouse while it's checked, it will draw everything darker and that's the on and pressed state. Not all controls have this of course, most for example, known as button typically doesn't.

Cell has some support for state but, frankly I think it's just as easy to do it yourself. So store the state in an instance variable and when you get a mouse down method, you can intrement the state and tell yourself to redraw and I don't think that's any more difficult than using NS cell.

So the inactive state is for when a control is in a background window and not all controls have this. If they do, it's usually subtle for an aqua control like this pop up button, it will lose the aqua appearance. It will just draw gray. The sort of metal button here will draw slightly less texture, there we go.

You can determine if you're in an inactive window by asking if your window is the key window. You could also ask if your window is, if your window equals the key window of the application. So you could call is key window or compare it to the applications key window. You'd think these might be the same, but they're often not. An example when they're not is in a drawer of a window.

In that case, the application has a key window which is the main, the window containing the drawer, but there may be multiple windows that respond yes from is key window. So this is the right way to do it. And then there's the disabled state which is when you draw faded and you don't respond to events.

NS controls has this is enabled method. This is not something you need to use a cell for of course. You do not even need to have give the control a cell to use the enabled state. But NS cell can help you draw things in a disable manner. So if you have say a string, cowabunga, it draws like this, you'd tell the cell hey I want you to be disabled, so disabled yes, it will draw it with a gray look instead of black and this works for images too.

So here we have a picture of the American flag and we call set disabled yes and it will draw it with less color. So this is an easy way to get a disabled state and this is another reason why NS cell is a good choice to use for drawing text and images inside your control as compared to string drawing primitives or NS image.

There's two more appearances I want to talk about that don't really fit in anywhere. There's this notion of roll over and this isn't something we encourage right, if every control had a roll over, it would be very distracting to be moving the mouse around the screen for every control to be you know, doing something every time you enter or leave it's rect.

So if you do use this, use it sparingly. An easy way to get a roll over effect is the set shows border only when mouse inside method. This is how for example, safari's bookmarks bar does it. You move over a bookmark in the bookmarks bar and it will invert the text and draw a bevel and it does this very easily with one line of code with the set shows border only while mouse inside.

But if you wanted to do a different roll over effect, this hasn't been something that's been easy in the past, good news is it's much easier in Leopard with the new tracking area class. So how would you do that? Well a tracking area is a replacement for tracking recs and the way you create one is you initialize it and you pass it some options. In this case, we're passing it the mouse entered and exited option, which indicates that we want to get notified when the mouse leaves and enters our bounds, as opposed to for example, getting notified every time the mouse moves within our recs.

And we're passing of the NS tracking invisible rect and that is within the visible region, not as in it is invisible and you can't see it. Because we're passing the invisible, the within visible rect flag, we can pass zero rect as the rectangle for the tracking area that will be ignored. It will always use the visible rect of your control, no matter how your control changes it's frame. So this is very convenient. Then you add it as a tracking area of your view.

When you get a mouse entered event, if a tracking area of the event is the tracking area you added, which you stored in an instance variable, then you indicate, you set the shows roll over flag yes and you tell yourself to read display and similarly for mouse exited, if the tracking area is the tracking area you added, you say I'm not showing roll over anymore and you tell yourself to read display. So this is a state you want to track manually.

And lastly, there is focus rings and of course, the focus ring is the familiar blue highlight around a control and it's very easy to determine if your control has the focus. You ask the key window of NS application, you get it's first responder and if that is your control, then you know you have the keyboard focus and you should indicate that in some way, particularly with the blue highlight.

So the way you draw the blue highlight is by saving the graphic state and calling the NS set focus ring style method and this means that all subsequent drawing will have the draw the focus ring around it. Now the slides are a little misleading here. Focus ring only indicates that all other drawing is blocked. So it only draws the focus ring. It does not draw other content that you tell it to draw while has this focus ring style.

If you pass the focus ring above, then it will draw both. And this is true, even if what you drew is not visible. So the way we can get a focus ring in track ball of course, QC view doesn't have any way of doing this easily and our track ball is not a rectangle, it is a sphere.

So we're just going to take the track ball, we'll just draw a clear circle around it, with the focus ring style it will draw the blue focus ring for us. Of course, this clear sphere does not show up and we're left with the focus ring around the track ball.

Now I think there's some confusion about the way focus rings work. I think people have this notion sometimes that it's floating over the window in a separate layer, but it's not. It is ordinary pixels in the same backing store as every other view draws into. The only difference is it's not clicked.

This means that it can draw outside the bounds of your control. Perhaps you've seen a case where one of your controls, you tap outside of it, there is a bit of focus ring left over, that because only the bounds of your control were redrawn. This means you have to take care to invalidate the proper region of the window, every time you gain or lose focus, which corresponds to become a resigned first responder methods.

So for example, in become first responder, if you can become first responder, you call through to super, then you indicate that the focus ring region should be redrawn with set keyboard focus ring needs display in rect and this will invalidate a bit more of the window than your control takes up. Invalidates just enough so that any focus ring you draw will be repainted and this is not something you have to do with every control, this is only for controls that do something custom with the focus ring like we saw with track ball.

So here's sort of a summary of the float control you might use for drawing, deciding which appearance to use. If your control is a first responder, then you will need to draw the focus ring. So you save the graphic state and call this set focus ring style. If your control is not enabled, you'll draw disabled. Otherwise if it's highlighted, you draw pressed. Otherwise if it's in a background window, you draw inactive and by default. If no other state applies, you can draw active And remember to restore the graphic state if you set the focus ring style.

Okay, that summarizes the different appearances you might have for your control. Let's talk about different behaviors you have as options for your control. Different choices you have to make about how your control behaves. And there's four that we're going to talk about. There's click through behavior, this notion of continuous controls, keyboard navigation and validation.

So click through of course, is whether a mouse click in an inactive window should not only bring the window to the front, but also cause the control to fire into action. And this is unusual because it depends not just on the type of control, but on what the control does. So for example in Mail, we have two buttons, a new message and the to do button and the to button does not have click through and the new message does.

Now why do we make this, how do you make this choice? Well it was talked about in the human interface guidelines, but to summarize, click through should be off for actions that are destructive like erasing a file or actions that can't be easily undone like sending an email or actions that destroys your context.

In the case of Mail, clicking the to do button does not send an email, but it does take you to a different part of Mail, where it lets you do edit this new to do. So in that sense, it destroys your context and that's why click through is off. The new message brings up a new window, but it doesn't do anything to the old window. It doesn't take you out of your context. That's okay to have click through.

Now click through is off by default and that is controlled but it is on in most NS control sub classes. So if you want click through to be on and your sub classing NS control, you do have to make ensure it's on, you have to do some work and track ball, it's harmless to click on the track ball and maybe move it a few pixels, so we're going to turn it on. The way you do this is in your control method to override accepts first mouse, your pass key event so you can make a decision based on the event. We don't care about the event, you normally don't. We'll just return yes.

So controls have this notion of a continuous action, which of course indicates that the control should continue to fire the action while the user is using the control instead of just firing it at the end and you make a control continuous by calling the set continuous on it of course.

And examples of continuous controls are a slider or a search field or a track ball, right a search field as you type it will filter as you, as you go. You don't have to wait, you know hit return at the end to make it filter. But this also might depend on the action of the control.

Like click through. For example, a volume slider can be easily continuous so you can hear the change in volume as you drag it. But a screen resolution slider, well it's very expensive to change the screen resolution. It's distracting. You only want to do that at the end when the user lets go. So a screen resolution slider would not be continuous.

Now continuous is free with NS cell's track mouse. So you call set continuous yes and your control will do the right thing. But I really meant action cell because, only action cell has target and action of course. How does NS control use this flag? Well we'll call up the first track until mouse up, we'll pass that to the cell. The cell within it's track mouse method, will send the action repeatedly during mouse tracking and again at the end. If it's not continuous, it'll only send it at the end.

Now if you use mouse dragged like our track ball does, it's much easier. You can just send the action yourself within the mouse drag method and that's another way to accomplish the same thing. I just wanted to give you an understanding of the way NS control and NS cell interact for making continuous actions.

All right. Keyboard navigation. So ideally if you're a control, the keyboard should be all that's necessary to get anything done. And the reason for that is accessibility. Users with motor skills disabilities, may have trouble using the mouse or a pointing device, but if they can use the keyboard and your control supports the keyboard entirely, then they can use your control fully.

Now of course, Cocoa does the key view loop for you so you can tab between controls, but it might have a case of one control that has sort of sub components that can gain focus. For example a tool bar, a tool bar can do that or an NS tab view. You can focus on each of the tabs even though they're part of the same control.

We saw that with track ball. You can tab to the various fields at the bottom. Now the human interface guidelines say that navigation occurs between controls with the tab, but within controls with the arrow keys. But of course, control here is as the users see it. So track ball, the user sees multiple controls, there's really one so we will use the tab, because it appears to be multiple controls.

So tab view and segmented control are example of where you use arrow keys to navigate within it and to accomplish this you can override move right and move left and these are methods that are called on you from NS window, when NS window receives key down event and as window performs it's mapping of keys to selectors and this is the Cocoa key binding's architecture, so you can, the user can configure different arrow keys or different keys entirely to correspond to move right or move left. Which is very convenient.

Between controls, date picker is an example of one control which has multiple apparent controls right. You can tab to the date picker and also to the up and down arrows on the right of side of it which controls in committing the date. And table view is another example of control that lets you navigate within the control view of the tab key and track ball as well. And of course, with track ball, we already use the arrow keys for manipulating the rotation so it's very, it makes sense to use the arrow key, the tab key for moving the focus within the control.

So the easiest way to get this sort of tracking or excuse me, keyboard focus within the control, is to just have an instance variable which tracks which part of the control has the keyboard focus. We just use an enum in track ball. We have the focus can be on the track ball itself or on any of the three fields and we store that as an instance variable within the track ball. We also define some convenient, you know, we're on the last part, where on the first part which will tell us when to you know, call through to super when we get the key down event.

So then in key down, we'll check the character we were passed and if it's the tab character and we're not already at the end, we're not focused on the last part, then we will just increment our focus portion which is the enum and tell us to redisplay and then when we draw again, we will draw the focus around the right part and if we get the back tab character which corresponds with shift tab and we're not at the beginning of the focuses part of our control, we will decrement the focus part and then tell us to redisplay and if we are at the beginning or end or if it's a different key, we will call through to super. Now the only reason we overrode key down, this is the tabbing, you do not have to override key down just to participate in the key view loop that Cocoa gives you for free.

All right, validation. So the user enters pizza like I entered in a text field, which is just for numbers, how do we prevent the user from committing this data? Well we do that by overriding text should end editing and in this case, we will call through to is entry acceptable which is the formatter of our cell, to determine if the user string matches the format. And if it does not, it returns no and we are not allowed to end editing the text field. So this brings us to right, you can also do other custom work of course and this text should end editing.

And this brings us to text editing and text editing is sort of an in depth topic. There's a lot of messages that are flying back and forth but NS cell again, makes it pretty easy to edit text fields and we'll see how. So how does text editing work? Well you'll have your control and your control has a cell that you can use for editing and this does not have to be the cell of your control, it could be an arbitrary cell and in the case of track ball, we have a cell we allocate just for the purpose of editing text fields. So this is arbitrary NS cell. And the control will call edit with frame or select with frame on the cell.

Okay, what happens next? This is how you begin editing. Well the cell is going to call out to the window of the control and say hey, I need the field editor and will pass yes for create. So the field editor will be created if it doesn't exist. Well the window then is going to allocate the field editor and the field editor is just an NS text view.

It's not a special class, sub class of NS text view, but it is configured with set field editor yes, which changes some of it's behavior. The text view will set your control as the delegate of the text view and it will add itself as a sub view of your control.

So now the text view is a sub view of your control and it's editing you and editing will continue until the field editor is done and the field editor will be done maybe if the user tabs out of the control or the user hits return or loses first responder in some other way.

For example, you close the window. And if text should end editing returns yes, which is the method we overrode, then it calls this on your control or on the delegate whatever you passed, then it will call text did end editing and this is your cue to fire your target in action and to call end editing on the cell you began editing with.

So your text at end editing is called on the control by the field editor and we call end editing on the cell and this tells the cell to remove the field editor as a sub view of your control and the editing session is done. So you set the target action, you've updated your object value and we're done editing.

So let's look at how track ball uses text editing for the mouse down event. If the mouse down is within the label region, the bottom portion of our control, then we want to begin editing. We will get the field editor, we will set our editing cell to have the value representative of whatever text field the user clicked on. So if the user clicked on the Y portion, that is the Y angle, we will set the editing cell to have whatever value the Y rotation is. And then we'll call the select with frame method.

We'll pass in view cell, so editing is happening within our control and we'll pass self as a delegate so that we get these call backs. And the difference between select with frame and edit with frame of course is that edit with frame will just begin editing I think with cursor appear to the end, but with select with frame, you have control over what portion is initially selected and we want to have the entire text initially selected so we pass the start of zero to indicate the beginning and the length of whatever the string value's length is. So we select the entire region.

Now in our text should end editing method as discussed earlier, we call through to the is entry acceptable method which uses the formatter and if the text did end editing method, this is when we need to update our object value and fire our target in action. And also to call end editing on the cell that we began editing with.

So that's the summary of text editing. Let's move on to accessibility. And accessibility of course is another topic where you can have multiple sessions on it, so I'll try to give you three rules to remember and the first rule is that accessibility is not, it's not frosting. It's not just bonus for your control, it's rather fundamental to your control and you should not treat it as something to add on sort of at the end.

Why is that? Well of course it's very important to make the control accessible to user with both visual disabilities or motor disabilities, but it's also for tool support. An example of a tool, this is new in Leopard which depends on accessibility, is watch me do an automator. So if your control is accessible, it makes it more useful not only to disabled users, but users without disabilities.

So that's why accessibility is important. The second rule is to think carefully about the role of your control. The role is the face of your control to users who are depending on accessibility and I think there's a tension when deciding on a role, oh you have to stick to the framework supplied in constant. You can return your own arbitrary role because tools won't know what to do with it.

And I think there's a tension between making your role descriptive and making you role accurate. What do I mean? Well for example, let's say you had a light switch control and you said hey, this light switch has got an on stage, it's got an off stage, this is kinda like the check box. So for my role I'm going to return NS accessibility check box role.

This may sound good at first, but imagine you had two users using the computer and communicating and one of them is depending on accessibility, one of them is sighted and looking at the screen. The first user might say hey, look at the check box, he thinks it's a check box because of the check box role, the sighted user's not going to see the check box, all the sighted user sees is the light switch. So a better choice for the role for the light switch is the value indicator role. It's not quite as descriptive, but it is more accurate and I think that is a better fit on the spectrum there.

So what role do we choose for our track ball? And this is, this is rather challenging, believe me. Our track ball is, oh it's the rules so think carefully about your controls rule, make sure it's descriptive and accurate and for the track ball, it's like a slider but, most sliders have an orientation of either horizontal or vertical and one way we can indicate that this isn't an ordinary slider is by returning an unknown orientation which is another constant you can return.

Now how do we represent the axis of rotation that the track ball shows. Well we're going to give it three children, so in the accessibility inspector for example, the track ball will have three children who's descriptions are the X rotation, Y rotation and Z rotation and these are value indicators indicating the rotation around these different axis.

And the third rule for accessibility is ideally, everything your control can do needs to be exposed through accessibility and this includes information about the control state and any properties that are settable and of course, any actions and your control also needs to send out notifications when the value changes. So in track ball, we have the accessibility hierarchy. We actually have nine children of the track ball. Right, so it's the slider, it's got the unknown orientation, it's got the three ordinary children, but we also want to expose the text fields as children as well.

So the X or the Y or the Z, the sort of static text labels to be exposed as static text role objects and the angles themselves will be text field role and these will be writable so that the accessibility, someone can modify their value, just the way we like when we typed in a value and we saw the control rotate to the final value.

And to set up the relationships properly, we will set the static text as the title UI element of the angle itself. Because it represents the label for that angle. So we have nine children as I mentioned. We have one child for each axis, one child for each label text field, each static text field and one child for each angle.

We accomplish this sort of, these fake children, these children are not part of the NSU hierarchy, but they are part, they are part of the accessibility hierarchy and we return what's called a faux UI element. This is not a class in the Cocoa frameworks, but it is a class in the sample code, in the track ball sample code and another sample code that is very useful for setting up accessibility hierarchy's that do not reflect the view hierarchy.

All right, so the sample code. We also must ensure that we send out notifications every time the object value changes or anything not just in set object value, but anytime we change it in any other method. So we send out an accessibility notification, not just for the angle, the axis element of the track ball, but also for the text field. This way is someone is preserving the text field or the angle, they'll get the right accessibility notifications.

Right, to sum up our track ball's accessibility, it does not match the view hierarchy and this is the more complex your control is, the less likely your view hierarchy and accessibility hierarchy are going to match. So that's okay, this doesn't indicate some sort of flaw in your design. You can get these phantom accessibility elements with the faux UI elements class, which I encourage you to incorporate because there are some complications in terms of is equal and hash, for faux UI elements.

We have one per axis, one per text field and one per label. So there's a total of nine. And we have to ensure we send out the notifications every time the track ball's object value changes. We have one for the text field and one for each axis that changes.

And with this accessibility design, I think we've reached the goal of everything that the track ball can do. It can also be done with accessibility. The user can modify the view rotations via the text fields or via the axis within the track ball itself. All right, so the three rules are all right, accessibility is important. It's not gravy, this is something which is fundamental to your control and fundamental to being a good citizen on Mac OS X.

Think carefully about the controls accessibility role attribute right, there's a tension between roles that are accurate and roles that are descriptive and you want to strike the right balance so you're not misleading, but you give enough information. And ideally, everything a user can do with a mouse or keyboard, should also be doable via accessibility. Either by the accessibility actions or by writable properties. All right.

Let's talk about bindings. Bindings is also an in depth subject. I'm just going to try to give you the enough information to set up basic bindings on your control. News you can use. But for more, you can of course attend a bindings session or the bindings lab and there is new bindings sample code which I'll also get to.

So how do Cocoa bindings work? Well you have your model object and you have your control and you say bind right. So I'm going to bind my control to my model and what happens? Well the control is going to observe the model of the key value observing and the model's going to observe the control. You might think that would make sense, but that's not actually how it works.

The control, the changes to the control are not pushed to the model automatically and they're not pushed by KVO. Instead the control has to explicitly set values on the model when it changes. Bindings are not symmetric, they are asymmetric and setting these values from the control back to the model, is custom per class.

Because the object value can change at any time and it can be also fairly tricky to code. It can be tricky to get this right and we don't have a great story about a way to do this right now. But the good news is, if your model is KVO and KVC compliant, then the other direction comes for free, thanks to NS object's implementation of the blind method. So any change to your model will be pushed to your control by the NS object's implementation of bind.

So the upshot of this is if your control is only used for displaying a value, it does not allow the user to modify the value, well then you're essentially done. All you have to do is ensure that your model is KVC and KVO compliant and you will properly reflect changes to the model into your view.

Now you have to be prepared for the set method of your control to be called. So if you bind the object value, then set object value will be called on your control and you have to remember to mark the control as needing display when this happens. Otherwise changes will not cause your control to redisplay.

Now if your view does change values, I recommend attending the bindings lab or seeing the bindings electric sample code which is new and this bindings electric sample code shows you how to push values from the view back to the model while taking into account the different bindings option that we have and that's a good place to look. The topic is too in depth for this session.

So that sums up bindings. Let's talk about some common pitfalls you might run into. What are some problems you might have when you are designing your control. So let's say you make your control and you find out that basic control methods don't seem to do anything. You call set font in your control and then you ask for the font back but you're getting a nil font, you're not getting the font you expect.

Why is that? Well as we discussed earlier, many of these NS control methods are just wrappers around NS cell. So if your control has a cell, these methods will work. If it does not have a cell, the methods will fail. So the solution is to call set cell class or override cell class so that NS control will instantiate and instance of the cell and set it on your control when the control itself is instantiated and then these methods will do what you expect.

And it can be a vanilla NS cell. It doesn't have to be a fancy NS button cell or text field cell or a custom cell. Even the basic NS cell has support for most of these methods. Use NS action cell for set target and set action. That's probably as deep into the hierarchy as you need to go.

All right, let's say you make your control and you find that it doesn't seem to work with first responder. So in IB, you set your controls target to first responder, but it doesn't seem to be working. Well a nil target indicates first responder. So when you set your target at first responder, that really means nil and of course, sending a message to nil doesn't do anything.

So instead of messaging your target directly, the right thing to do is to call through to NS app and pass and pass NSApp send action, pass your actions to pass your target from pass your control. And NS app will take care of not only sending the target or sending the action to the first responder if the target is nil, but it will also do the right thing for model panels, for many target actions should be blocks. So definitely use send action to from, instead of messaging your target directly and track ball does this.

The last pitfall. Say you pressed the arrow keys, you pressed the right arrow key and you expect move right to be called. Move right does not seem to be called on your control. What's going wrong? Well, these methods are called by NS Window on your control via the Cocoa key bindings architecture. They are not called directly.

So if you override key down and you do not call through to super, then NS window will not receive the event and know to call move right on your control. So of course, for actions, key presses that you do not handle, make sure you call through to super and then you will have these move right methods called on your control.

All right, let's wrap up everything we've discussed and we'll take questions. So custom controls are not really difficult. They are straightforward. They make sense. They really do. So don't worry too much about creating one, it's not that hard. You can use sub views to help you draw. We saw that with track ball. It used the QC view to draw the track ball itself. You can also use cells to draw, but you don't have to sub class NS cell typically. The one case where we saw you did have to sub class NS cell was for prefers tracking until mouse up.

You could use cells for drawing, for mouse tracking, for text editing. Don't be afraid of NS cell. It can seem like an intimidating class and maybe one that's not easy to understand, but it's not that bad. It's not hard to use and it's very useful. Even plain vanilla NS cell is very useful.

We saw all the different appearances. There's the press appearance, there's the on state disabled and think carefully about which ones apply to your control, so I want to have a roll over, how do I draw my focus rings. And we saw how to determine which appearance applies at any given moment. And think carefully about the different behaviors. Should my control be continuous, should it support click through, what is the that should be configurable if the action is also configurable. Because click through is something which depends not only on the class control, but also on the action.

And don't forget accessibility right. Accessibility is not an add on at the end. Accessibility is fundamental to being a good control on Mac OS X. Not only for users depending on it, but for tools support. And I'm sure most of you have, but if you haven't, please read the human interface guidelines. It will make your control a better citizen on Mac OS X and it will give you better understanding of the way, the reason existing controls work the way they do.