Day 33: NSResponder actions (2)
In the first article I outlined the problem one faces; you can’t write your code in one view subclass because it won’t always be the first responder. You also can’t subclass the one responder all have in common (the window) since that’d be terrible coding practice. The solution is of course to inject something that handles these actions in the responder chain and instead of injecting one big file with all my actions I put each action in a separate class. I think that’s the sensible route.
Another requirement I had was that things had to work well with toolbars. In Sketch I have quite a busy toolbar and I wanted to construct the toolbar from my actions. If at all possible, the base class for my actions should be smart enough to validate a menu item and create a toolbar item for itself among other things. This was quite easy as long as I adhered to a few basic conventions.
Let me start by giving a trivial example. I’ve called my base class MSBaseAction (MS is the prefix I use, a relic from the past). Say I want to add a vectorize command to Sketch together with a matching toolbar item. Now I thought it would make sense to call this action MSVectorizeAction and the action my menu-item will connect to - (IBAction)vectorize:(id)sender. Note that each action must have a unique selector otherwise the responder chain message forwarding will get messed up.
I later thought that actually it would be a good idea to be able to programmatically exectute an action (eg. MSVectorizeAction) without having to know its specific action name (-vectorize:). Given the conventions I followed it was easy to create a -performAction method that just calls the right action name: (and I’m paraphrasing)
- (void)performAction
{
NSString *name = [self className];
NSRange range = NSMakeRange(2,[name length]-8); //len(MS) == 2, len(Action)==6
NSString *flatName = [[name substringWithRange:range] lowercaseString];
[self performSelector:NSSelectorFromString:([flatName stringByAppendingString:@":"])
withObject:nil];
}
As you can see here, we have a practical example of getting the class name as a string and turning a string into a selector. Tomorrow I’ll give a similar example of how to create a toolbar item in very much the same way.