Tutorial: Storyboard in XCode 4.2 with Navigation Controller and Tabbar Controller (Part 2)

Hello again!  If you’re reading this tutorial I’m going to assume that you read part 1 and are ready to continue.  In this tutorial we will take the project from part 1 and expand the second tab (the table) to include some actual table data and a transition to a ‘detail’ screen.  In this instance I’m using movie names and movie images to make it look pretty.  There’s a tiny bit of coding taking place here, but nothing exceptional.  We won’t be making use of Core Data at this stage, just to keep things simple.  Lets get on with it shall we?  (*keep in mind this was written in a hurry, but has been tested!*)

Before we begin, you’ll need the original project (where we left off in part 1) and the table images. You can get them both here…

Get the project from part 1 here – [download id=”6″ format=”1″ autop=”false”]

Get the table images here – [download id=”7″ format=”1″ autop=”false”]

 

Getting Started / New View Controller

First of all, unpack the images zip file you downloaded above and drag the images into the Supporting Files group in your project.  There should be eight of them (0 through to 7).  Once you’ve dragged in those images we need to create a new View Controller.  We’re going to use this for our ‘detail view’ once a movie has been selected in the table.   So, create a new file (File -> New -> New file).  Choose UIViewController subclass.  Give it a class name of Tab2_ItemViewController and untick both boxes.  Good stuff.

We may as well set up the code for this new class now (it’s not complex).  So open up Tab2_ItemViewController.h and change its content to this….

#import 

@interface Tab2_ItemViewController : UIViewController
{
    NSString *selectedItem;
    NSInteger selectedIndex;
    IBOutlet UILabel *outputLabel;
    IBOutlet UIImageView *outputImage;
}

@property (nonatomic) NSInteger selectedIndex;
@property (nonatomic, retain) NSString *selectedItem;

@end

 

As you can see, all we are doing here is creating some instance variables we will use to hold our selected movie name (selectedItem) and the table row index (selectedIndex) which we will use to display the related image. We also define some outlets for Interface Builder to display the details.  Now open up Tab2_ItemViewController.m and change its content to this…

#import "Tab2_ItemViewController.h"

@implementation Tab2_ItemViewController

@synthesize selectedIndex, selectedItem;

- (void)viewDidLoad
{
    [super viewDidLoad];

    [outputLabel setText:selectedItem];
    [outputImage setImage:[UIImage imageNamed:[NSString stringWithFormat:@"%d.jpg", selectedIndex]]];
}

@end

 

Nothing much going on here either really.  Note though, I’ve stripped out all the predefined boilerplate junk which I won’t be using for the demo.  What we’re left with is the viewDidLoad method which is simply setting a label and image which we’re going to create in Interface Builder in just a moment.

 

Creating a data source for the table

Next we are going to edit our TableViewController to give our app somewhere to grab it’s data.  Ordinarily I’d use Core Data for this purpose but wanted to keep this fairly simple.  Instead, we are going to use a very basic array which will hold a bunch of movie names (Kevin Smith movies – my favourites!).   Let’s do the edits first and then i’ll explain what’s going on.  Open up Tab2_TableViewController.h and change it to the following…

#import 

@interface Tab2_TableViewController : UITableViewController
{
    NSMutableArray *myData;
}

@end

 

Again, nothing spectacular going on here.  We are simply declaring an array to store our table data.  Now open up Tab2_TableViewController.m and change it to the following….

#import "Tab2_TableViewController.h"
#import "Tab2_ItemViewController.h"

@implementation Tab2_TableViewController

// When the view loads, define our data
- (void)viewDidLoad
{
    [super viewDidLoad];

    // Define our test data
    myData = [NSMutableArray arrayWithObjects:
              @"Chasing Amy",
              @"Mallrats",
              @"Dogma",
              @"Clerks",
              @"Jay & Silent Bob Strike Back",
              @"Red State",
              @"Cop Out",
              @"Jersey Girl",
              nil];
}

// Return number of sections in table (always 1 for this demo!)
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

// Return the amount of items in our table (the total items in our array above)
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return [myData count];
}

// Return a cell for the table
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // A cell identifier which matches our identifier in IB
    static NSString *CellIdentifier = @"CellIdentifier";

    // Create or reuse a cell
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    }

    // Get the cell label using its tag and set it
    UILabel *cellLabel = (UILabel *)[cell viewWithTag:1];
    [cellLabel setText:[myData objectAtIndex:indexPath.row]];

    // get the cell imageview using its tag and set it
    UIImageView *cellImage = (UIImageView *)[cell viewWithTag:2];
    [cellImage setImage:[UIImage imageNamed:[NSString stringWithFormat:@"%d.jpg", indexPath.row]]];

    return cell;
}

// Do some customisation of our new view when a table item has been selected
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    // Make sure we're referring to the correct segue
    if ([[segue identifier] isEqualToString:@"ShowSelectedMovie"]) {

        // Get reference to the destination view controller
        Tab2_ItemViewController *vc = [segue destinationViewController];

        // get the selected index
        NSInteger selectedIndex = [[self.tableView indexPathForSelectedRow] row];

        // Pass the name and index of our film
        [vc setSelectedItem:[NSString stringWithFormat:@"%@", [myData objectAtIndex:selectedIndex]]];
        [vc setSelectedIndex:selectedIndex];
    }
}

@end

 

Okay, again I’ve stripped out a lot of boilerplate code which you may see in your original file and littered some comments around.  In our viewDidLoad method, we are defining the contents of our data array (the items which will appear in the table) – eight items in total.  Next, we return number of sections and number of rows in the table.  Following that, we have our cellForRowAtIndexPath: method which returns a single cell for the table.  In this method we either reuse or create a table cell and populate it with a title and an image which we will set up in a moment.  I’m using the ‘tag’ value to identify elements in my table cell.

Finally, we come to a new method for iOS5 called prepareForSegue:sender: which is an important addition to the storyboarding.  This method is linked by a specified identifier to a segue from the storyboard (you know those link thingies which link all your views together).  The method is called after your view is initialised and before it is displayed, so we use this method to do some customisation (in this instance, we’re using it to pass the title of our selected movie and the selected table index row so we can figure out which image to use.   So stepping through this method, you see that we first check to see if the segue identifier matches the one we will set in just a moment.  Then we grab a reference to the view controller we’re about to display (remember, at this stage its already been initialised).  Then we simply set the instance vars we defined earlier.

And that’s all the coding that’s required.  The rest we will do in the storyboard.

 

Storyboard Revisited

Right, so now we only have to hook everything up and we’re ready to roll!   Open up the storyboard file again and click on the (Tab2 Table View Controller) table view.  Under ‘style‘ choose grouped.  I prefer grouped tables for looks.  Now select the blank (prototype) cell and in the Identifier box, type in CellIdentifier (no spaces).  This must match exactly the name used in the cellForRowAtIndexPath: method.  If we were to run at this point we would see a blank table with eight entries but no data.  We need to put a label and an image view into that blank cell.  Drag in a UILabel from the Objects list (Utility panel) and put it somewhere in the blank cell.  Do the same with a UIImageView.  This should automatically resize to around 35 x 35 pixels when dragged into the empty cell.  Arrange it something like this…

I’ve chosen to put my image view on the left and my label on the right (with right justification).  Feel free to be creative with your layout!  Now, most important – we’re going to use the Tag value to identify these two elements when we draw the table content, so first select your label and change Tag value to 1 (you can find Tag under the Attributes Inspector – third icon from right in the Utility panel).  Next, select the UIImageView and change its tag value to 2.   Go ahead and run your project – under Tab 2 View you should now see a rather satisfying table complete with titles and images…

When we select a row, it’d be nice to see how to push a new view with some details.  We’re only going to show the same label and image in its own view – but you could of course, use this for anything at all (editing etc).  Earlier on we created a new class called Tab2_ItemViewController.  It’s already locked and loaded for use here, so lets drag a brand new UIViewController just to the right of our table.  Once it’s in place, select it and in the Identity Inspector (third icon from left in utility panel) change the class to Tab2_ItemViewController.  Now go back and select the cell in the table view (making sure it IS the cell you select and not the label!) and CTRL+Click drag (or right-click drag) to our newly created View Controller.  Choose ‘Push‘ from the submenu.  You’ll notice the navigation bar will be automatically added – why not take this chance to double click the bar and give it a title…. say “Movie Details” or something.   Something else happened here which you may not have noticed.  When we connected the table cell, it was automatically given a disclosure indicator (the chevron on the right hand side).  Just make sure it’s not being overlapped by your label or image and adjust if needed.

Hang in there…. almost done.  On your new view controller, drag in a UILabel and a UIImageView.  You can make the image view 70 x 70 pixels in the Size Inspector if you like.   Arrange them however you like.   I’ve made my label the width of the view and centred.  I’ve put the image right underneath it – and made the whole view background colour a horrid pink.   When you’re done, we need to connect these two elements up.  Simply CTRL+Click drag (or right-click drag) from the status bar (where the battery icon is!) to the label and choose outputLabel.  Then again from the status bar to the image view and choose outputImage.

And now the final, final step.  Remember earlier in our code where we specified a name for our Segue?  Well we now need to name that Segue.  Click the connection between the tableview and the view controller and in the Attributes Inspector, for the identifier enter the name ShowSelectedMovie.  Remember it must match exactly the one we used in the prepareForSegue: method earlier.   Here’s what the finished storyboard should look like roughly (except perhaps the pink background!)

That’s all!  We’re done.  Quick…. go and try your work of art.   Everything should function perfectly…. if you select a table row, we will now see the movie details.  Job done!

You can get the finished project here!  Please comment if you enjoyed the tutorial or it helped you out.

[download id=”8″ format=”1″ autop=”false”]

Comments are now closed for this tutorial but I’d still love to hear from you. You can post in our integrated forum by clicking this link (or by going to the Discussion Forum tab at the top of the page)