33 Comments

Easier Multiple Storyboards in iOS with Custom Segues

storyboard-example

One of our latest iOS projects has quite a few view controllers. A few weeks ago, John Fisher wrote about the pain of large storyboards and our solution of using multiple storyboards, but we’ve found an even better solution. This technique is more reliable, easier to use and has less code. Win, win, win!

Custom Segues to the Rescue

John’s post described RBStoryboardLink, which uses a clever system to dynamically re-parent a view controller. We ran into problems with this because it didn’t perfectly handle auto layout or dynamic navigation bar visibility. A better solution is to dynamically change the destination view controller before a segue starts. That allows iOS to do all the view layout calculation without any problems. We wrote a custom segue, AOLinkedStoryboardSegue, to do just that.

Xcode 5 supports custom segues quite nicely. They appear on the menu when you control-drag to create a segue, so your custom segues are as easy to use as one of the built-ins. Xcode even translates the Objective-C class name to something more readable. Our AOLinkedStoryboardSegue class appears as linked storyboard.

Lastly, we wanted an easy way to set the destination view controller. Segues don’t have any editable properties, so we repurposed the identifier to be the name of the view controller you want to link to using the syntax controller@storyboard. If you omit the controller name, it will link to the initial view controller.

Here’s a short video showing how to push from one storyboard to a view controller in another storyboard. The steps are:

  1. Drag a placeholder view controller onto the storyboard.
  2. Control-drag to create a segue to the placeholder.
  3. Choose “linked storyboard” on the segue menu.
  4. Select the newly created segue.
  5. Edit the segue identifier to be the linked view controller, in this case it’s the initial view controller in destination.storyboard, so enter @destination.

Show Us the Code Already!

The header file AOLinkedStoryboardSegue.h doesn’t really need to publish the helper method for loading a view controller, but we found it to be useful in a couple other places.

#import <UIKit/UIKit.h>
 
@interface AOLinkedStoryboardSegue : UIStoryboardSegue
 
+ (UIViewController *)sceneNamed:(NSString *)identifier;
 
@end

The class file AOLinkedStoryboardSegue.m just swaps out the placeholder view controller with the real destination. We only use push transitions between storyboards, but you can sub-class this or change the segue identifier syntax to also specify the transition style.

#import "AOLinkedStoryboardSegue.h"
 
@implementation AOLinkedStoryboardSegue
 
+ (UIViewController *)sceneNamed:(NSString *)identifier
{
    NSArray *info = [identifier componentsSeparatedByString:@"@"];
 
    NSString *storyboard_name = info[1];
    NSString *scene_name = info[0];
 
    UIStoryboard *storyboard = [UIStoryboard storyboardWithName:storyboard_name
                                                         bundle:nil];
    UIViewController *scene = nil;
 
    if (scene_name.length == 0) {
        scene = [storyboard instantiateInitialViewController];
    }
    else {
        scene = [storyboard instantiateViewControllerWithIdentifier:scene_name];
    }
 
    return scene;
}
 
- (id)initWithIdentifier:(NSString *)identifier
                  source:(UIViewController *)source
             destination:(UIViewController *)destination
{
    return [super initWithIdentifier:identifier
                              source:source
                         destination:[AOLinkedStoryboardSegue sceneNamed:identifier]];
}
 
- (void)perform
{
    UIViewController *source = (UIViewController *)self.sourceViewController;
    [source.navigationController pushViewController:self.destinationViewController
                                           animated:YES];
}
 
@end