Cocoa, Core Data, and me (VI)

We’re still working to get the country browser right, and it isn’t right yet.

The countries browser.

I don’t want the table in the browser to be editable, that is what the drawer is for. So when you doubleclick on a row in the browser, I want the drawer to slide out, if it’s not visible, and to have the focus set to the first field in the drawer. After editing is finished, I want the drawer to close again. If you click the “+” button, I also want the drawer to open so you can enter a new record. If you click the “-” button, I want the row to be deleted without any “Are you sure? Are you really sure? Are you really, really, really sure?” dialogs. After all, we have (or will have) full undo available.

Implementationwise, I want the doubleclick and button commands to go to the controller object, and from that object back to the UI and the model. That’s the “correct” way of doing things. Right now, the “+” and “-” buttons go directly to the array controller, which isn’t right. We’re going to change that.

First, we have to disable editing in the browser table. Open Interface Builder and doubleclick the table view. If you click just once, the scroll view will be selected, showing little blue dots around the area while you’ll see “NSScrollView Inspector” in the inspector’s titlebar:

Selecting the scroll view only.

If you doubleclick the table, the actual NSTableView will be selected, which you can see in the inspector’s titlebar. At the same time, the frame around the table looks different:

NSTableView selected in IB

Finally, select the first column by singleclicking its header:

Selected first column.

Move your attention over to the inspector and uncheck the “Editable” checkbox:

Unchecking 'Editable'

Then click on the second column header and uncheck that same checkbox for that one as well. From now on, you can’t edit directly in the table view and any doubleclicks we give it can be forwarded elsewhere.

Great. Now we need a method in the CountriesController to call when the table is doubleclicked. That method needs the following signature:

Function signature

How do I know that? I’d love to give you an erudite explanation on how you could easily deduct this from the online documentation, but I can’t. Honestly, I used Google and found this article on Apple’s Developer Connection. Doing exactly as they say, it works, even though I can’t explain every step of it yet. The short and long of it is that you should now implement the method in CountriesController, like this (just to have something we can check if it is getting called or not):

Method in CountriesController

To set up things according to that article, open Interface Builder, select the table view by doubleclicking it. In the inspector pane, select “Bindings”, expand the “doubleClickTarget” line and fill it in according to the article. You are effectively replacing the array controller target with your own target in the CountriesController. In particular, note the “Selector Name” field at the bottom (I just sat there staring at the screen for minutes before it registered in my muddled brain), and take care to add the trailing colon (if you don’t, the program throws an exception and shows you an assembler listing of something almost certainly very interesting but incomprehensible; I know, I stared at that for another couple of minutes as well). This is what it should look like:

Setting the doubleClickTarget

Now open the “doubleClickArgument” section and change it like so:

Setting the doubleClickArgument section.

Save it all, build and run and you should see the “doubleClick” in the log.

I assumed that wiring up the doubleclick action to the CountriesController would involve declaring an IBAction and then control-dragging in IB to hook it up, but that doesn’t work. Instead you have to enter selectors in the bindings pane in the NSTableView inspector. To me, this seems like two different ways of doing similar tasks. Only goes to show I haven’t understood much of this Cocoa deal yet. But sooner or later I’ll have my eureka moment.

As you can see from the previous text, we’re passing an array of selected objects (rows) to the method handling the doubleclick. We won’t need that, since the content of the drawer is already synchronized with the selection in the table through the array controller, but it can’t hurt, so we’ll leave that in.

The doubleclick handler

The doubleclick handler in the CountriesController should open the drawer and give it focus. To do that, it needs to know where to find the drawer controller, an NSDrawer which is in the nib file. If we do this as an outlet, cocoa will automatically set the pointer once the NSDrawer is instantiated, saving us some coding.

First, add the outlet to the CountriesController header file (an outlet is nothing more than an object pointer, but recognizeable to Interface Builder since it has a keyword IBOutlet):

Adding the outlet

Save the header file, then do the drag-and-drop thing yet again to let IB scan the file:

Dropping the header file on the nib pane.

Up pops a question, asking if you want to replace or merge. I always answer “merge” and that seems to work:

Parse dialog

Now control-drag from “File’s Owner” to the “DetailsDrawer” object in the Countries.nib pane:

Controldragging to details drawer.

And, hey presto, up pops the inspector with “File’s Owner” in the title bar and a list of outlets, one of which will be called “drawer”. Either doubleclick that row or click the “Connect” button, so you get a gray dot in the leftmost column. The “Connect” button also turns into a “Disconnect” button when the outlet is connected.

Connecting the outlet in the inspector.

Go back to the doubleclick method implementation in CountriesController.m and add in a call to the drawer to open itself:


Save, build, run. Test it out: doubleclick any row in the countries table and the drawer slides open. Phew.

How did I know about the “open” method in NSDrawer? Well, I looked up NSDrawer in AppKiDo, a highly recommended freeware app that makes finding stuff in Apple’s documentation much easier:

AppKiDo screen shot.

Go get it now.

Are we done for today? Hmmm… not really. I need to move the focus to the right field, close the drawer when finished, wire up the “+” button right, etc. But, frankly, I’m tired. That will have to wait to another day.