Ian McCullough has a good post up that was sparked by my half serious and half intentionally silly rant about how best to use the iOS responder chain. I think he mischaracterizes my position but that is through no fault of his own. I didn’t make an argument, I just wanted to blow off a little steam after having spent far too long getting something to work as it should. The problem I encountered exhibits itself in of the most professional, well documented and consistent codebases I’ve worked in. I’ve worked in a lot. I want to be clear — the problem isn’t someone being a goof, it’s that the way this stuff all fits together on iOS is problematic and often opaque.
I’m an Interface Builder advocate. In many ways IB is what sold me on Cocoa in the first place. The fact that it didn’t auto-generate code was simply incredible. The precise problem I encountered was that a button in a table view cell subclass was sending an action message to the First Responder.
In the vast majority of cases a button sending an action up the responder chain is amazing and a true boon of the way AppKit and UIKit work. It becomes problematic when the button is a child view of a table view cell. The receiver of the action cannot know which particular cell invoked the action. Since the odds are if you’re responding to an action from a list of items you’re very likely going to want to know which one of them to act upon.
There are solutions. If your view controller is the one who catches the action sent to the First Responder then it has all of the information it will need to work out which model item needs to be acted upon. But if the action goes any higher up the responder chain then subsequent responders simply don’t have the context they need to do the right thing. They can’t decode which item is being interacted with because they don’t have the table view to hand, or possibly even the data source, to figure it out. These situations are rare but they do happen.
Ian seems to think I advocate a delegate approach. I don’t. It can work and is suited to when you need far more information to be passed than a simple action message can provide but, by and large, that’s not what I’d recommend.
I’m a fan of the responder chain. It’s been a terrific idea for a long time. In the places where it doesn’t work we can figure out how to use it better. In this case, of a button sending an action message to First Responder, from a custom UITableViewCell subclass I’d change things up a little. First, the actions of the buttons in the table cell would target the UITableViewCell subclass I had. When the table view cell was asked to perform an action it could simply pass it up the responder chain with itself as the sender. With the simple convention of an
-(id)representedObject method (or Protocol if you want to be fancy) we can at least glean from the sender which item to act upon. This has the benefits of leveraging the responder chain as it was designed to be (and what it’s great at doing) while providing operational context for the action method that will eventually be invoked.
Effectively, this turns the UITableViewCell subclass into a micro-controller object. In practice this is both convenient and honest. The action is not coming from some button. It’s coming from the table view cell asking you to perform some action on the data it represents. You would be shocked if something like UISearchBar fired off action messages up the responder chain and the sender was something like the little cancel button on the right hand side. Could you figure out what the intention was? If you had the view hierarchy, yeah, probably. But it’d be goofy.
The responder chain in both UIKit and AppKit is a very good thing. Leverage it as best you can. Just beware of the limitations.