-[UIViewController targetViewControllerForAction:sender:] method (and its related methods,
-showDetailViewController:sender:) in iOS 8 takes a clever responder chain-based approach to showing view controllers in a size-class-adaptable way.
UIViewController instances respond to
-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
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: 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!