Navigating back and forth among Viewcontrollers in Storyboard

In the old days if you wanted to present a ViewController you should call presentModal.. or push… in the code.  The problem with this approach is that if you want to modify the presentation you should change the code.

To separate this Apple found out the Storyboard – all your ViewControllers in one place. Visually representing the ViewControllers and their relations.
You can control on the Storyboard how different ViewControllers are presented – the code of a ViewController is totally free of how another ViewController is presented. A segue describes what to present (from where to where) and how. You can bind a control’s touchUpInside to a segue for example. If a segue navigates backward it is called unwind segue. You can give them a name and you can fire it manually (from code).
To pass data to a ViewController override the:
 – (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender method.

Unfortunately not everybody knows the unwind segue. This is about to navigate backward in the presentation chain. With unwind segue you can navigate back not only to the previous ViewController but back to any other ViewController in one step! For this you have to create a method in a ViewController you want to navigate back with a special signature:
– (IBAction) unwindToXYZViewController:(UIStoryboardSegue*)segue.

This method can be used to access the data in the presented ViewController.
You should name it properly to know where you want to navigate back. XCode will present you all the unwind segues when you want to bind one – and you have to select a good one 🙂 Just controler+drag from the control to the third button (Exit) on top of the ViewController in storyboard and select the method in the presenting list.

The only big question what Apple did not solved is the multiple unwind segue. Imagine that two ViewController (A and B) presents the same (C) ViewController. Now when you want to create an unwind segue for example pressing a button you can bind only one segue. So how can you navigate back to the proper ViewController?

Below is the answer I figured out.

What you have to do is to create two manual unwind segues. One for each ViewController you want to navigate back. To do this control+drag from the first button on top of the ViewController to the third one.
Now the tricky part: you have to manually fire the proper segue. For this you have to decide which is the previous ViewController – which one presented the “C” ViewController. Unfortunately Apple don’t provide any simple property or method.

So I created a category method on the UIViewController which gives back the proper one.
Here is the code snippet:
[expand title=”click to show code snippet”]

@implementation UIViewController (HelperMethods)

- (UIViewController*) previousViewController {
    UIViewController *viewControllerToReturn = nil;
    
    // !!!: navigation should have the preference as if a navigation was opened modally and this viewcontroller is the third pushed then the presenting will be the one opened the modal
    if (self.navigationController!=nil)
    {
        NSUInteger index = [self.navigationController.viewControllers indexOfObject:self];
        if (index > 0)
        {
            --index;
            viewControllerToReturn = self.navigationController.viewControllers[index];
        }
        else if (index == 0) // if this is the root in the navigation return the modal presenter of the navigation
        {
            // to be sure check the instances...
            if (self.presentingViewController.presentedViewController == self.navigationController)
            {
                viewControllerToReturn = self.presentingViewController;
            }
        }
        // else NSNotFound. what the hell?
    }
    else if (self.presentingViewController.presentedViewController == self) // make sure this viewcontroller is the modally presented one
    {
        viewControllerToReturn = self.presentingViewController;
    }
    
    // TODO: handle other controllers: Tab Bar, Page etc,
    
    return viewControllerToReturn;
}

@end

[/expand]

Now to fire the proper unwind segue:

UIViewController* previousViewController = [self previousViewController];
    
if ([previousViewController isKindOfClass:[AViewcontroller class]]) {
    [self performSegueWithIdentifier:@"UnwindToAViewControllerSegueId" sender:self];
}
else if ([previousViewController isKindOfClass:[BViewController class]]) {
    [self performSegueWithIdentifier:@"UnwindToBViewControllerSegueId" sender:self];
}

The nice in it that you can freely change the presentation of the ViewControllers