-targetViewControllerForAction:sender: is smarter than it seems

The new -[UIViewController targetViewControllerForAction:sender:] method (and its related methods, -showViewController:sender: and -showDetailViewController:sender:) in iOS 8 takes a clever responder chain-based approach to showing view controllers in a size-class-adaptable way.

Basically, all UIViewController instances respond to -showViewController:sender: and -showDetailViewController:sender:, but their implementations use -targetViewControllerForAction:sender: to find the appropriate view controller to actually do the showing. In the case of -showViewController:sender:, this is likely an enclosing UINavigationController, and in the case of -showDetailViewController:sender:, this is likely an enclosing UISplitViewController.

But according to the documentation, this shouldn’t actually work. -targetViewControllerForAction:sender: is documented to use -canPerformAction:withSender: to determine an eligible target. That method, in turn, is documented to return YES if the receiver responds to the given selector.

If that’s all true, then -targetViewControllerForAction:sender: should always return self when given @selector(showDetailViewController:sender:)! But it clearly doesn’t—it returns an enclosing UISplitViewController, if one exists. So what’s going on?

Well, the docs for -targetViewControllerForAction:sender: lie. -targetViewControllerForAction:sender: does indeed walk the view controller hierarchy, sending -canPerformAction:withSender: on the way. But if a view controller returns YES, it will then determine whether the instance’s method for that selector is an override of a UIViewController implementation for the same selector. If not, it keeps looking up the chain.

This is why UISplitViewController is able to ensure it is returned for -targetViewControllerForAction:@selector(showDetailViewController:sender:) instead of any intermediate view controllers between itself and the sender. It’s also why applications are able to mimic UIKit’s pattern with their own custom actions, which they wouldn’t if UIViewController instead relied on a hardcoded list of selectors.

I’ve filed documentation feedback with Apple. But in the meantime, remember that -targetViewControllerForAction:sender: is smarter than it seems!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s