Useful Cocoal.icio.us Code: The “setAction:forKey:” Method

It’s been a long time since I really discussed Cocoa programming on this weblog, and since Cocoal.icio.us is now open source, it seems germane to talk about some of the project’s code. I’m particularly fond of the simple bits of code that make life so much easier, and I think one of the best examples of this in Cocoal.icio.us is the “setAction:forKey:” and “actionForKey:” methods found in the SFHFTableView class.

These methods were written because I wanted to provide quick, arrow key navigation between views (the tag table and the posts table) along the lines of what NetNewsWire and Address Book do. I was somewhat surprised, however, to find that Cocoa has no generalized way of associating actions (selectors) with key events. Rather, subclassers must override NSResponder’s “keyDown:” method to detect key presses and create a big switch statement to associate particular keys with their appropriate actions.

Since I’m always a proponent of spending the extra time to solve a problem in the general case, I created an NSTableView subclass and implemented “setAction:forKey:,” which stores an association between a character and a selector in an NSDictionary instance variable. When the view’s “keyDown:” method is called, it queries “actionForKey,” which returns the selector specified for the given character, and if one exists, calls it (otherwise it simply passes the event on to the superclass’s “keyDown:”).

With this mechanism in place, the key actions for a view can be easily configured from outside the view itself, by placing lines like the following in the application delegate’s “awakeFromNib” method, for example:

[tagList setAction: @selector(makePostListFirstResponder) forKey: NSRightArrowFunctionKey];

There are a few things about this technique that are less than optimal in my opinion—it doesn’t take into account key modifiers (although I check [[NSApp currentEvent] modifierFlags] in my event handler to get around that) and it can’t be “injected” into NSResponder itself via a category since it requires an instance variable—but all in all, I think this way of doing things is a lot more elegant and Cocoa-like than the alternative.

Leave a Reply