34

I'm getting a crash when trying to set a property in my segue. It's a UIView passing a property to a Navigation Controller that has a TableView as it's root view. It's supposed to go to my TableViewController, but it looks like it is getting intercepted by that NavigationController and throwing an unrecognized selector error.

Segue:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([[segue identifier] isEqualToString:@"showItems"]) {
        ShowItemsTableViewController *destinationViewController = [segue destinationViewController];

        [destinationViewController setItems:[self itemsFromCoreData]];
    }
}

Error:

-[UINavigationController setItems:]: unrecognized selector sent to instance 0x10920c840

What's going on here? That NavigationController doesn't have a class associated with it, it's just in the storyboard and comes up modally. It works if I set the segue in the storyboard to go directly to the view, not the NavigationController, but I really need the nav there. How do I get around this?

4 Answers4

75

Since the destination view controller is actually the navigation controller, try accessing the root view like so:

UINavigationController *navController = [segue destinationViewController];
ShowItemsTableViewController *SITViewController = (ShowItemsTableViewController *)([navController viewControllers][0]);
[SITViewController setItems:[self itemsFromCoreData]];
Kamaros
  • 4,536
  • 1
  • 24
  • 39
  • 1
    I'm guessing this would be similar for a tab bar controller than too? Thanks for the help. –  Nov 04 '13 at 18:46
  • I see that sometimes people get the right VC and sometimes don't. Very odd. Is there some sort of config somewhere that tells the application to get the final destination view controller? – mikebz Jun 07 '14 at 15:47
  • 1
    You can also use [navController topViewController] – Labokas Nov 19 '14 at 11:02
  • @mikebz, I have 2 segues, both are modal, one is from a cell, the second is from a bar button item. One gives me a navigation viewcontroller and the second gives me the final destination view controller. Quite strange. – mezulu Nov 27 '14 at 19:32
  • 3
    In swift: `segue.destinationViewController.topViewController` – Kevin Qi Mar 19 '15 at 20:23
  • If you do this a lot and you'd like to decouple your code a bit form knowing about storyboard changes, you may want to add a category method on `UIStoryboardSegue` that determines if `destinationViewController` is a `UINavigationController` and if so return its `topViewController` but otherwise, just return it directly. – Benjohn Apr 30 '15 at 15:58
8

Get the topViewController from the UINavigationController:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if ([[segue identifier] isEqualToString:@"showItems"]) {
        UINavigationController *navigationController = segue.destinationViewController;
        ShowItemsTableViewController *showItemsTVC = (ShowItemsTableViewController * )navigationController.topViewController;
        showItemsTVC.items = [self itemsFromCoreData];
    }
}
Aamir
  • 16,329
  • 10
  • 59
  • 65
Guto Araujo
  • 3,824
  • 2
  • 21
  • 26
8

For Swift:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if segue.identifier == "segueShowNavigation" {
        var DestViewController = segue.destinationViewController as! UINavigationController
        let targetController = DestViewController.topViewController as! ReceiveViewController
    }
}
Stiño
  • 2,663
  • 7
  • 25
  • 47
0

Look at the view controller class in the prepare for segue - it's the nav controller. Cast it to that class, then you can get to the desired view by asking it for its top view controller.

David H
  • 40,852
  • 12
  • 92
  • 138