iPhone Dev: Accessing a Selector in Another ViewController

This had me up for a few nights, so here is my solution. I think someone at #iosDevCamp asked me this same question, too – I think it was Sophia.

Problem: You have an iPhone app with a UIRootViewController, tab navigation, with a UIButton that launches a UITableViewController (a second view). When a user selects a row in the table, and you want that action to affect something on the RootViewController.

Real world example: My main root view controller has a movie player (UIMoviePlayerViewController). When a user selects the button on the tab “movies,” I want that selected movie to start playing back in the root view controller, and collapse the current table view. The code for starting the movie is in the RootViewController, so I need to pass it the command to start playing, and the name of the movie.

The way that works for me is:
Setup a method in the root view controller.

// all the movie goodness here

In my UITableViewController’s “did select”, I access the Root ViewController through… wait for it… took me forever to figure this out… the NavigationController.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[[self.navigationController.viewControllers objectAtIndex:0] didTap];
[self.navigationController popToRootViewControllerAnimated:YES];

Some crafty things: the root controller will always be the first in the array, “viewControllers.” “Pop to root” is a convenient way to slide the tableview off and display the movie again. I actually read the Class documentation for that one!

On the iPad, I can simply refer to it as [appDelegate.viewController didTap] (Thanks to Scott Tran for that simple, so seemingly obvious, way of doing it). The Navigation/Tab stuff complicates things, I take it.

How do you pass an object?
There are various ways. I chose to setup a property in the app delegate, and set that value from the tableviewController, so I didn’t pass it along to the RootViewController with the method, but you could:

[[navController.viewControllers objectAtIndex:0] didTap:myObject];

I’d like to also say- you can setup a NSNotification, which is quite easy and works.

I realize it’s a bit controversial to access a method in another UIViewController. Best design sense says to build it in the appDelegate and refer to that from each view. I was 90% done with this app and not interested in refactoring.