4
votes

There must be a simple solution to this, but after 4 hours of browsing StackExchange, I'm giving up and trying to get this answered here:

Situation: iOS 5, Xcode 4.2.1, using Storyboards

Setup:

  • Root Navigation Controller = UITabBarController with 5 items
  • Each tab links to a UITableViewController (via segues), which works perfectly, automatically
  • I have classes for each view controller and the navigation controller (UITabBarController)
  • I click on a tab, say "Program", and it shows me a list of items (in the linked UITableViewController), say one of these items is "Sessions"
  • I click on "Sessions", didSelectRowAtIndexPath gets called just fine.
  • I want to perform a segue now (which I have connected from that UITableViewController, not the cell, to the new UITableViewController and given it an identifier.
  • I call: [self performSegueWithIdentifier:@"programToSessions" sender:self];

  • prepareForSegue gets called, but nothing afterwards. No error, no warning. The new UITableViewController doesn't show up.

  • Note: It DOES show up, however, if I switch the segue to a "modal" - but it discards/hides the original tab bar at the bottom then (not intended...)

So far, my problem seemed to have a likely solution. The navigation controller. I figured, if I setup a navigation controller reference in the AppDelegate, initiated/set it once the actual root navigation controller is loaded (in viewDidLoad), i.e. basically used something along the lines of: app.navController = self.navigationController in the viewDidLoad - this could work! Right? So, I changed the segue action to: [app.navController performSegueWithIdentifier:@"programToSessions" sender:self];

Yet, nothing changes, it does not show up.

Any ideas?

Solved:

There were a few issues here. Major issue was the fact that I missed putting a "Navigation Controller" between the UITabBarController and the UITableViewController. This can be done by adding a new "Navigation Controller" to the storyboard, then drag from UITabBarController (the rootViewController) to this new controller and set a RELATIONSHIP. Next, drag a RELATIONSHIP to the UITableViewController. So, as outlined by the correct answer it should look like: UITabBarController --> (relationship) --> UINavigationController --> (relationship) --> UITableViewController

In order to load new UITableViewControllers on top of the last UITableViewController, a simple segue and calling it through code will suffice. The final tree will look like:

UITabBarController --> (relationship) --> UINavigationController --> (relationship) --> UITableViewController --> (segue) --> UITableViewController

Give the segue an identifier. In code, use this:

     - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
        [self performSegueWithIdentifier:@"loadMyDetailView" sender:self];
    }
2
Your solution worked for me. You should post it as an answer so people can vote it upPhil Hale

2 Answers

3
votes

You have to use prepareForSegue:sender: to fire the equivalent of didSelectRowAtIndexPath:. (Empty out the latter.) Instead of pushing the new view controller on the navigation stack, the segue already has a property destinationViewController that will just work automatically. You can modify this property if you want to customize the new view controller. Make sure segue.identifier is correct!

Also, from your question I notice some confusion about the controllers of view controllers. You mention two of these: the tab bar controller and a navigation controller. In storyboard, the setup should be like this:

TabBarController --(relationship)--> NavigationController 
--(relationship)--> ViewController 
--(segue)--> next ViewController

Make sure all these are correct in storyboard and see if it works.

1
votes

You don't have to performSegue programmatically. You could create a segue from the first UITableViewController to the second on the storyboard, by control dragging from the UITableViewCell of the first UITableViewController to the second UITableViewController. If the segue is not performed, its most likely reason is that, cell identifier in the method

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

and in the "Attributes Inspector" do not match.

To pass data from the the first UITableViewController to the second add the following method to the first:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([[segue identifier] isEqualToString:@"loadMyDetailView"]) {
        MyDetailViewController *detailVC = [segue destinationViewController];
        NSIndexPath *selectedRowIndex = [self.tableView indexPathForSelectedRow];
        detailVC.dataToPass = [self getDataToPass:selectedRowIndex.row];
    }

}