Use Your Loaf

[[brain engage] write]

Storyboard Segues

I have previously posted on some of the pros and cons of adopting various storyboard features in an existing App. In writing the post on prototype table cells it occurred to me that I have said nothing about one of the key features of storyboards - segues. Using storyboard terminology a segue is the transition between scenes (views). In this post I want to compare using a storyboard segue with a traditional view transition.

Classic View Transitions

For the purposes of comparison I am first going to add a classic, non-storyboard, transition to the WorldFacts App I used for demonstrating prototype table view cells. To recap we have a table view that shows country data stored in a core data model. The root view is shown below:

WorldFacts Example App

What I want to do is add a view transition for when a row is selected in the table that transitions to a detailed country view controller. The detailed country view should end up looking as follows:

WorldFacts Country Detail

This is a classic iOS design pattern so I will cover it quickly. First of all I will create a NIB file (UYLCountryViewController.xib) to contain the view and a corresponding view controller (UYLCountryViewController). The view controller is pretty simple, it has a single public property to contain the Country object that it is supposed to display:

@interface UYLCountryViewController : UIViewController
@property (nonatomic, strong) Country *country;
@end

I will not bother to show the implementation as it is mostly just setting the label text for each element in the viewDidLoad method. Check out the example project code if you are interested in the details.

Now the standard iOS pattern for a view transition from a table view consists of implementing the UITableViewDelegate method tableView:didSelectRowAtIndexPath::

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
  Country *country = [self.fetchedResultsController objectAtIndexPath:indexPath];
  UYLCountryViewController *viewController = [[UYLCountryViewController alloc] 
                            initWithNibName:@"UYLCountryViewController" bundle:nil];
  viewController.country = country;
  [self.navigationController pushViewController:viewController animated:YES];
}

The basic steps for a table row view transition can be summarised as follows:

  • Using the indexPath of the selected table row find the corresponding object.
  • Allocate the detail view controller and initialise it from the NIB file.
  • Pass the selected object to the detail view controller
  • Push the view controller onto the navigation stack

The code directly related to the view transition is contained in a single method with four lines of code. No big deal but worth keeping in mind when we implement the same functionality using storyboards.

Storyboard Seque

To implement the table view transition with storyboards we first need to drag a standard view controller into our country storyboard (refer back to the post on prototype table cells for the creation of the storyboard). The configuration of the view controller follows the same steps as for the standalone NIB implementation. We set the class of the controller to be UYLCountryViewController and add and connect the UILabel outlets.

To create the segue/transition you control-drag from the table view cell to the destination view controller and when prompted select the style of the transition (push, modal or custom). In this case we want a standard navigation style push transition. The storyboard should at this point look as shown below:

country.storyboard

To complete the storyboard we should set an identifier for the seque using the identity inspector:

segue identifier

Now to actually implement the segue view transition from the table view controller we no longer implement the UITableViewDelegate method (tableView:didSelectRowAtIndexPath:). A big difference to the classic approach we saw earlier is that with storyboard segues we no longer need to create the destination view controller or initiate the push transition ourselves. The storyboard takes care of all of that boilerplate code for us. However since we need to pass some data to the destination view controller (in this case the country object to be displayed) we need to implement prepareForSegue:sender::

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{ 
  if ([segue.identifier isEqualToString:UYLSegueShowCountry])
  {
    NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
    Country *country = [self.fetchedResultsController objectAtIndexPath:indexPath];        
    UYLCountryViewController *viewController = segue.destinationViewController;
    viewController.country = country;
  }
}

In this simple example our view controller only has a single segue from a table view cell. The segue argument is an object of class UIStoryboardSegue which we can use to both check for the seque identifier we set in the storyboard and also to obtain a reference to the destination view controller that we need to configure.

It is not strictly necessary for us to set and check for the segue identifier in this case as we have only one. In more complicated scenarios with multiple segues triggered from multiple sources we can use the segue and sender arguments to differentiate.

The sender argument in this case is a reference to the UITableViewCell that the user selected to trigger the segue. We need to get the country object that corresponds to the selected table view cell so that we can pass it to the destination country view controller. This requires two-steps, the first to ask the table view for the NSIndexPath that corresponds to the source table view cell. Once we have the index path we can then get the country object as before using the fetched results controller. We can then retrieve the destination view controller from UIStoryboardSegue object and pass it the country object. The storyboard takes care of everything else for us.

Wrapping Up

If we compare this storyboard approach with the previous non-storyboard version there is not much in the way of code saving. We avoid having to create and push the destination view controller but in the end it still takes us about the same amount of code to configure it. In more complex scenarios the need to check a long list of segue identifiers to determine which destination view controller we need to configure can also start to look a little ugly.

Whilst there is not much in the way of code saving with segues the advantage is that you do get to see the way your interface flows in the storyboard. That may not be a big enough advantage to make you want to leap into using storyboards everywhere in your app but it helps. I should of course also mention the obvious constraint that storyboards are an iOS 5 (and now iOS 6) feature.

As always the example Xcode project can be found in my CodeExamples GitHub repository if you want to check the details for yourself. I have left the original NIB files in the project for comparison even though they are no longer required.

Comments