Chapter 2

Introduction to Interface Builder

One of the many changes introduced in Xcode 4 was the consumption of Interface Builder into the main Xcode application. Interface Builder became a core component of Xcode and was able to run in the same windows and tabs as Xcode. The change was more than just a simple embedding of one application into another. As you will see in this chapter, the way that Interface Builder interacts with your source code makes it more of a well-intentioned integration into Xcode.

Interface Builder Walkthrough

When you click on a .xib (user interface) file in the navigator pane of Xcode, Interface Builder will seamlessly load into the editor pane. Generally, when I’m working with .xib files, I will close the navigator pane and show the utility area on the right. This gives me the most screen real estate for visually creating the interfaces, as shown in Figure 2–1.

Image

Figure 2–1. Interface Builder in use

By clicking an object in the outline view dock on the left side, you can see the object’s attributes and settings in the inspector pane on the right side. The inspector pane should be very familiar to anyone who has worked with Interface Builder before. The object browser has also been integrated into the library pane below the inspector pane. This creates one-stop shopping for your .xib design needs.

Our Forces Combined…

With the integration of Interface Builder into Xcode, it extends beyond just two tools in one. Just as in the cartoon Voltron, when the combination of individual tiger robots resulted in an incredible defender of the galaxy, Interface Builder when combined with the Assistant Editor view creates a super tool for source code. Figure 2–2 shows a prime example of this useful combination.

Image

Figure 2–2. Using Interface Builder with the Assistant Editor

When the Assistant Editor is visible and you select one of the objects in the .xib’s object browser on the left, any existing header file for the associated view controller is loaded. At first this appears to be minimally useful. You can see your code and the .xib file. The real magic comes when you ^-click-drag one of those objects to the interface (.h) file. As Figure 2–3 shows, a blue line will extend into the code, and when you release it in the right pane of the Assistant Editor, you will be prompted to create an outlet.

Image

Figure 2–3. Connecting an outlet automatically

When you release the mouse button, Xcode will prompt you for the name of this outlet connection, as shown in Figure 2–4. After you specify a name and click Connect, Xcode will create all the necessary code to connect the .xib’s object to your interface (.h) file and implementation (.m) file.

Image

Figure 2–4. Configuring an outlet’s creation

The header file now looks like this:

//
//  sample1ViewController.h
#import <UIKit/UIKit.h>
@interface sample1ViewController : UIViewController {
    UILabel *labelHelloWorld;
}

@property (strong, nonatomic) IBOutlet UILabel *labelHelloWorld;

@end

And the relevant code from the implementation (.m) file now looks like this:

//
//  sample1ViewController.m
#import "sample1ViewController.h"

@implementation sample1ViewController
@synthesize labelHelloWorld;

- (void)viewDidUnload
{
    [self setLabelHelloWorld:nil];
    [super viewDidUnload];
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
}

@end

You can use the same steps to create IBActions for buttons and other objects. ^-click-drag from a button or similar object in the .xib to the implementation (.h) file and release. This time, change the drop-down menu for Connection from Outlet to Action. If this option does not appear, the wrong type of object was selected. Figures 2–5 and 2–6 detail the process of configuring an action in this way.

Image

Figure 2–5. Creating an action

Image

Figure 2–6. Configuring an action

You can specify the name of the action, the type, the event to trigger the action on, and the arguments to send. Just like the Outlet connection, the code will be updated with placeholders to support your new action.

The interface (.h) file now includes the IBAction declaration:

//
//  sample1ViewController.h
#import <UIKit/UIKit.h>

@interface sample1ViewController : UIViewController {
    UILabel *labelHelloWorld;
}

@property (strong, nonatomic) IBOutlet UILabel *labelHelloWorld;
- (IBAction)tickleTheLabel:(id)sender;

@end

And the implementation (.m) file includes a method placeholder for you to complete:

- (IBAction)tickleTheLabel:(id)sender {
}

I’m going to complete the tickleTheLabel method with the following code:

- (IBAction)tickleTheLabel:(id)sender {
    self.labelHelloWorld.text=@"That tickled";
}

Figures 2–7 and 2–8 show that now, when the app is run and the button touched, the labelHelloWorld is updated.

Image

Figure 2–7. Original view

Image

Figure 2–8. View after action performed

Touches Too

One of the new features implemented into Interface Builder is the ability to assign touch gesture recognizers to objects directly in Interface Builder. The various gestures are available in the lower right section of Xcode, resembling the pane shown in Figure 2–9.

Image

Figure 2–9. Available gesture recognizers

To add a gesture recognizer to an object in a .xib, just drag and drop the gesture recognizer from the object browser onto the object you want to add it to, similarly to that shown in Figure 2–10. This will add the gesture recognizer to the Objects list in the .xib project file, as shown in Figure 2–11.

Image

Figure 2–10. Adding a gesture recognizer to an element

Image

Figure 2–11. Resulting gesture recognizer in the outline view

Before you proceed, it’s important that user interaction is enabled for the object that you want to add the gesture recognizer to. In this case, you attached it to the Hello World label, and you can set user interaction to true by clicking the label and going to the Attributes inspector tab. Under the View options, click the check box next to User Interaction Enabled, as shown in Figure 2–12. This will ensure that the object responds to gesture recognizers.

Image

Figure 2–12. The Attributes inspector

By clicking the gesture recognizer in the .xib’s dock outline view, you can set the settings for the gesture recognizer in the Attributes inspector pane. Figure 2–13 demonstrates the various configurations that can be applied to this recognizer.

Image

Figure 2–13. Adjusting attributes of gesture recognizer

Now that the settings have been enabled, you can connect the gesture to an IBAction by following the same procedures as ^-click-drag from the gesture recognizer object to the interface (.h) file in the Assistant Editor pane. In the resulting pop-up, resembling Figure 2–14, specify a name and click Connect. The placeholders for that action will be added to your code just as before.

Image

Figure 2–14. Connecting an action to the label

Now your interface file looks like this:

//
//  sample1ViewController.h
#import <UIKit/UIKit.h>

@interface sample1ViewController : UIViewController {
    UILabel *labelHelloWorld;
    UITapGestureRecognizer *tapTheLabel;
}

@property (strong, nonatomic) IBOutlet UILabel *labelHelloWorld;
- (IBAction)tickleTheLabel:(id)sender;
- (IBAction)tapTheLabel:(id)sender;

@end

And I’ve made the new “tapTheLabel” action in the implementation (.m) file look like this:

- (IBAction)tapTheLabel:(id)sender {
    self.labelHelloWorld.text=@"Tap Tap Tap";
}

Now when the app is run and the label is tapped, you get the following two screens in Figures 2–15 and 2–16.

Image

Figure 2–15. Application’s starting view

Image

Figure 2–16. View after label tapped

Adjusting Tint

If you wanted to give your app a different look and feel by customizing the navigation bar or tool bar, you used to have to create your own custom classes; but in Xcode 4.2, you can now customize certain design elements with the new tint property. UINavigationBar, UIToolBar, UISearchBar, and UISegmentedControl all respond to this setting and are available in Interface Builder. Other controls respond to tinting as well, but the property is not available in Xcode and must be changed with code. To update the tint, select an object in your .xib that supports the tint property, and in the inspector pane you should see the Tint control, as shown in Figure 2–18. Figure 2–17 demonstrates a view with drastically altered tint on several elements.

Image

Figure 2–17. A view with tint

Image

Figure 2–18. Adjusting tint in the Attributes inspector

NOTE: The UISegmentedControl supports tint in Xcode only in the Bar or Bezeled style.

Rapid App Development with Storyboarding

Remember the days when you had to use paper and pen to sketch out design flows for your apps? Then came flowcharting software, in which you could digitally record your workflows and processes, but it was a manual process to convert those workflows into source code. Apple has provided a new tool called Storyboards that provides a visual representation of an app’s workflow and can then produce a working framework for your app.

So What’s in a Story(board)?

A storyboard is a collection of .xib files packaged together along with some metadata about the views and their relationships to each other. It is the ultimate separation of views from models and controllers that you have been hearing about since the early days of Model-View-Controller (MVC) programming. The storyboard has two main components: scenes and segues.

Scenes

Scenes are any view that fills the screen of the device. They contain UI objects and are controlled by view controllers (or subclasses of view controllers). This is almost exactly like the .xib files that you are familiar with editing in Interface Builder. Figure 2–19 displays three different scenes in a storyboard that you will soon build.

Image

Figure 2–19. Editing scenes

Segues

Segues are the transitions that present subsequent views in a storyboard. The segue can present a view with a push, as a modal view, as a pop-over, or with a custom transition. A segue is of the class UIStoryboardSegue and contains three properties: sourceViewController, destinationViewController, and identifier. The identifier is an NSString that can be used to validate that a segue is the segue you are expecting.

You would normally initiate a segue based on an action from the user. This can be the touching of a button or tableview cell, or it could be the result of a gesture recognizer. Segues are represented on the storyboard by a line connecting two scenes, as shown in Figure 2–20.

Image

Figure 2–20. A segue connecting two scenes

Telling a Story

Storyboards are available in all of the application templates in Xcode 4.2 with the exception of the empty project template. When creating a new project, just select the option to “Use storyboard” when setting the options for the new project. In this case, you will name the project “aboutUs”, as demonstrated in Figure 2–21.

Image

Figure 2–21. Configuring for storyboard use

Once the project is created, you will see the storyboard option has been populated on the target Summary tab and an additional field has been added to the Info tab (as shown in Figures 2–22 and 2–23), “Main storyboard file base name”.

Image

Figure 2–22. Main storyboard info

Image

Figure 2–23. Storyboard target settings

Finally, in the project navigator pane, you will see the MainStoryboard.storyboard file, as Figure 2–24 demonstrates. Click on this file to load it into Interface Builder and start building your storyboard.

Image

Figure 2–24. Storyboard file

In this example, you are going to build a simple project that displays information about your company. The storyboard starts off with a view that is controlled by the aboutUsViewController it created as part of the project. I’m going to add some objects (UILabel, UITextView, and two UIButtons) to the view to make it a little more informative to the user. Refer to Figure 2–25 for the view to build.

Image

Figure 2–25. aboutUsViewController view

Now I want to embed this view into a navigation controller, and Xcode 4.2 makes this an easy task. As shown in Figure 2–26, select the view and go to the menu option Editor Image Embed In Image Navigation Controller. This will create a navigation controller and add it to your storyboard as well as create a segue between the navigation view and your aboutUsView. The resulting segue will be represented with an arrow, as in Figure 2–27.

Image

Figure 2–26. Embedding a view in a controller

Image

Figure 2–27. Resulting embedded view display

Now I’m going to add a new UIViewController object to the storyboard where you can put your contact information. It’s as simple as dragging a UIViewController object, as well as a UIView, from the object library to the storyboard. I set up the view by adding a UILabel as the heading, and then I add a few more UILabels for the contact information. Figure 2–28 displays the result of these additions.

Image

Figure 2–28. Configured scenes

In order to connect the new contact information view to the About Us view, you are going to click on the “Contact Us” UIButton on the About Us view and ^-click-drag to the Contact Info view. This is the same action used to connect outlets and actions, and that is exactly what you are going to do. You are going to connect the Contact Us button to the performSegueWithIdentifier action. When you release the mouse button, a pop-up will display, asking which action you want to connect to, and you can select performSegueWithIdentifier:sender. These steps, along with the resulting segue, are demonstrated in Figures 2–29 and 2–30.

Image

Figure 2–29. Configuring segue action

Image

Figure 2–30. Perform segue pop-up

When the connection is made, the UINavigationBar is automatically added to the view. If you specify titles for each one, the result will resemble Figure 2–31.

Image

Figure 2–31. Connected scenes with segue

One habit to get into is providing your segues with an identifier. This will help future-proof your apps if you end up connecting multiple segues to one view. You will be able to check the identifier of the calling segue to see the path the user took to reach that view and respond accordingly. You can set the identifier of a segue by selecting it in the storyboard and viewing its properties in the inspector pane, as shown in Figure 2–32.

Image

Figure 2–32. Setting segue identifiers

If you run this app now, Figures 2–33 and 2–34 show you that the Contact Us button will work and will load the Contact Info view without having written any code whatsoever.

Image

Figure 2–33. Main simulated view

Image

Figure 2–34. Resulting segue performed

What about the other button, “Our Apps”? You want to create a new view that lists your other apps so you can get some cross promotion. The first thing I think of when I hear the word “list” is UITableViewController. And storyboarding takes UITableViewController to a whole new level of convenience.

I’m going to drag a UITableViewController to the storyboard, creating an Apps Table view. The first thing that you will notice is that this looks a little different than the regular UITableViewController available in Interface Builder. There is something called Prototype Cells at the top, as in Figure 2–35. With storyboards, you can customize the layout and objects of a UITableViewCell with something called a prototype. We’ll go into this further later on.

Image

Figure 2–35. Inserting a UITableView into a scene

Select the Table View, and in the Attributes inspector, change Content from Dynamic Prototypes to Static Cells. I’m also going to change the style to Grouped because I like the look of the rounded edges of the cells. Figures 2–36 and 2–37 show these steps and their result.

Image

Figure 2–36. Static Cells configuration

Image

Figure 2–37. Grouped cells

Since every cell is going to have the same layout, I’m going to delete the bottom two cells so that I can quickly duplicate the top cell. Now I will customize the remaining cell with a UIImageView to hold the app icon and two UILabels for the app name and description, as shown in Figure 2–38.

Image

Figure 2–38. Customized cell

Select the remaining cell now that it has been designed to your specifications, and Image-click-drag to duplicate it below. Repeat again to add a third row to the UITableView, as demonstrated in Figure 2–39.

Image

Figure 2–39. Duplicating cells

Now you can customize each image and label in the UITableView to list the three apps you’ll be displaying, resulting in a view resembling Figure 2–40.

Image

Figure 2–40. Customizing duplicated cells

All that is left is to connect your button in the About Us view to this new view. Select the button in the About Us view, and ^-click-drag to the UITableView you just created. Your storyboard now looks something like that shown in Figure 2–41.

Image

Figure 2–41. Newly connected table controller

And when you run the application, both buttons on the About Us view will now work, demonstrated in Figure 2–42. And you can load the pages without ever having written a bit of code.

Image

Figure 2–42. The three resulting views of your application

Passing Data Between Scenes

The previous app segment works well enough without any code, but just by adding a little bit of code behind the scenes, you can create an even more powerful interface in a very short period of time.

When you see a UITableView, you almost instinctively know that there is likely to be a detailed view attached to it when you touch one of the cells. Let us add that detail view now. Drag and drop a new UIViewController object named AppDetailsViewController onto the storyboard, and add a UIImage, UILabel, UITextView, and two UIButtons to it, resembling Figure 2–43.

Image

Figure 2–43. User interface for detail view

You want each of the UITableViewCells to segue to this App Details view when touched, so ^-click-drag from the UITableViewCell to the detail view. Just as before, a pop-up will display the available actions, and you should select “performSegueWithIdentifier:selector:”, as demonstrated in Figures 2–44 and 2–45.

Image

Figure 2–44. Connecting a cell to the detail view

Image

Figure 2–45. Selecting a segue action

You will see the segue connecting the table to the App Details view controller. Just as is shown in Figure 2–46, select the segue and enter an identifier for it in the Attributes inspector.

Image

Figure 2–46. Configuring detail segue identifier

Repeat the process for the other two UITableViewCells, and use the same segue identifier for each connection since you are going to execute the same code for each segue. You should now see three segues linking the two views, similar to Figure 2–47.

Image

Figure 2–47. Multiple segues connecting a UITableView to a view controller

You’ve gotten this far without using any code, but that convenience is about to end. You need to start generating some dynamic content on the App Details view controller, and you are going to need to dive into some code for that.

First you are going to create a custom class to hold information about your apps. Since this is a static list of apps at the moment, I’m going to create a very basic subclass of NSObject to hold the data. Use the menu option FileImageImage New Image New File…, and select “Objective-C class” from the Cocoa Touch templates, as demonstrated in Figure 2–48.

Image

Figure 2–48. Selecting the “Objective-C class” template to make a new basic class

Now, as shown in Figure 2–49, select NSObject from the “Subclass of” drop-down if it is not already specified.

Image

Figure 2–49. Ensuring the “Subclass of” field is specified to “NSObject” to create an Object subclass

And finally, set the class name to MyAppClass. This is shown in Figure 2–50, but just as before, newer versions of Xcode may have this combined with the previous step of specifying the “Subclass of” field.

Image

Figure 2–50. Specifying the class name—on newer versions of Xcode, this may be included in the previous screen.

You want this class to have the following interface (.h):

//  MyAppClass.h
#import <Foundation/Foundation.h>

@interface MyAppClass : NSObject

@property(strong, nonatomic) NSString *appName;
@property(strong, nonatomic) UIImage *iconImage;
@property(strong, nonatomic) NSString *appDescription;
@property(strong, nonatomic) NSURL *appStoreURL;
@property(strong, nonatomic) NSURL *webSiteURL;

+(MyAppClass *)appWithAppID:(int)appID;

@end

And the implementation file (.m) should look like this:

#import "MyAppClass.h"

@implementation MyAppClass

@synthesize appName;
@synthesize iconImage;
@synthesize appDescription;
@synthesize appStoreURL;
@synthesize webSiteURL;

+(MyAppClass *)appWithAppID:(int)appID{
    MyAppClass *newApp=[[MyAppClass alloc] init];
    newApp.appName=[NSString stringWithFormat:@"App %i Name", appID];
    newApp.iconImage=[UIImage imageNamed:[NSString stringWithFormat:@"app%iicon.png",
appID]];
    newApp.appDescription=[NSString stringWithFormat:@"This is the description for App
%i", appID];
    newApp.appStoreURL=[NSURL URLWithString:[NSString stringWithFormat:@"itms-
apps://itunes.com/apps/%iappName", appID]];
    newApp.webSiteURL=[NSURL URLWithString:[NSString
stringWithFormat:@"http://www.shawnsbits.com/apps/%iappName", appID]];
    return newApp;
}

- (id)init
{
    self = [super init];
    if (self) {
        // Initialization code here.
    }
    
    return self;
}

@end

NOTE: If you are wondering where all the memory management has gone, see the section on Automatic Reference Counting, called “Steve and the ARC,” in Chapter 1.

So that sets up your data object, but now you need to code up your detail view controller to display the attributes of the MyAppClass object. You will need a custom view controller class to control the view. Create a new file with FileImageImage NewImageImage New File…, and select “UIViewController subclass” from the Cocoa Touch templates, as Figure 2–51 demonstrates.

Image

Figure 2–51. Selecting the “UIViewController subclass” template to create a file pre-configured with important UIViewController methods

Now, as in Figure 2–52, make it a subclass of UIViewController (if not specified by default), and make sure that the check box for “With XIB for user interface” is not selected. You will be using your storyboard for the XIB.

Image

Figure 2–52. Configuring a UIViewController subclass

Name the new class AppDetailsViewController, and use the following interface (.h) file definition:

//  AppDetailsViewController.h

#import <UIKit/UIKit.h>
#import "MyAppClass.h"

@interface AppDetailsViewController : UIViewController

@property (strong, nonatomic) MyAppClass *selectedApp;
@property (strong, nonatomic) IBOutlet UILabel *labelAppName;
@property (strong, nonatomic) IBOutlet UIImageView *imageAppIcon;
@property (strong, nonatomic) IBOutlet UITextView *textViewAppDescription;
@property (strong, nonatomic) IBOutlet UIButton *buttonAppStore;
@property (strong, nonatomic) IBOutlet IButton *buttonWebSite;

-(IBAction) loadAppStore:(id) sender;
-(IBAction) loadWebSite:(id)sender;

@end

This interface file will create the outlets that you need for your view and also create the two IBActions that will be assigned to your UIButtons. The implementation file (.m) looks like this:

//  AppDetailsViewController.m

#import "AppDetailsViewController.h"
@implementation AppDetailsViewController

@synthesize selectedApp;
@synthesize labelAppName;
@synthesize imageAppIcon;
@synthesize textViewAppDescription;
@synthesize buttonAppStore;
@synthesize buttonWebSite;

-(IBAction) loadAppStore:(id) sender{
    [[UIApplication sharedApplication] openURL:self.selectedApp.appStoreURL];
}
-(IBAction) loadWebSite:(id)sender{
    [[UIApplication sharedApplication] openURL:self.selectedApp.webSiteURL];
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.labelAppName.text=selectedApp.appName;
    self.imageAppIcon.image=selectedApp.iconImage;
    self.textViewAppDescription.text=selectedApp.appDescription;
}


- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)didReceiveMemoryWarning
{
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];
    
    // Release any cached data, images, etc. that aren’t in use.
}

- (void)viewDidUnload
{
    [super viewDidUnload];
    self.labelAppName = nil;
    self.imageAppIcon = nil;
    self.textViewAppDescription = nil;
    self.buttonAppStore = nil;
    self.buttonWebSite = nil;
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    // Return YES for supported orientations
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

@end

This controller class will populate the view with the properties of the MyAppClass object “selectedApp”. Now you need to attach this view controller to the view and connect the outlets and IBActions. Go back to the storyboard editor, and select the view controller object at the bottom of the App Details view, as shown in Figure 2–53.

Image

Figure 2–53. Selecting a view controller

Now you can select the Identity inspector in the inspector tab and set the class to your new custom AppDetailsViewController class. Figure 2–54 demonstrates how to do this.

Image

Figure 2–54. Specifying a view controller

Connect all of the objects to their corresponding outlets, following Figure 2–55.

Image

Figure 2–55. Configuring elements to properties

To connect the actions, you reverse the process. You ^-click-drag from the UIButtons to the view or view controller object and select the corresponding IBAction from the pop-up, as in Figure 2–56.

Image

Figure 2–56. Connecting actions

You can now accept a MyAppClass object and display it properly on the App Details view, but now you need to send this object when you load this view. To do this, you will configure the previous view (Apps Table View list) to send the object when it performs the segue. It turns out that there is a method for this; it is called -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender;. You can override this method in a custom UITableViewController to send the selected object.

First, you will need a new custom UITableViewController class. Create a new file with File Image New Image New File…, and select “UIViewController class” from the Cocoa Touch templates, as shown in Figure 2–57.

Image

Figure 2–57. Specifying a UIViewController subclass file

This time, you want it to be a subclass of UITableViewController, and again, make sure that you do not create a XIB for the new class, as in Figure 2–58.

Image

Figure 2–58. Configuring a UITableViewController subclass

Name the class “AppListTableViewController”. There are no changes needed for the interface file (.h), so you will jump straight to the implementation file (.m). The first thing you want to do is clear out the methods for the tableView data source and tableView delegate because you are using statically defined UITableViewCells in your storyboard. So delete or comment out the following methods:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath
*)indexPath

At the top of your implementation file, import the two custom classes you created:

#import "AppDetailsViewController.h"
#import "MyAppClass.h"

Now override the prepareForSegue method with the following code:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
    if([segue.identifier isEqualToString:@"AppDetailsLoadFromTableViewCell"]){
        AppDetailsViewController *appDetailsVC = segue.destinationViewController;
        appDetailsVC.selectedApp=[MyAppClass appWithAppID:[[self.tableView
indexPathForSelectedRow] row]+1];
    }
}

This code checks to ensure you are responding to the correct segue in case you add additional segues in the future. Afterward, it acquires the currently selected row of the tableView and adds 1 to it (because your apps start at 1 but your row counts start at 0). It then creates an object of class MyAppClass using that appID and the convenience method appWithAppID: that you created. This new object is assigned to the selectedApp property of the destinationViewController (an instance of AppDetailsViewController).

Before proceeding to test this application, make sure that the AppListTableViewController has been set as the class for the App List table view controller.

If you run your app now, you’ll see that each of the UITableViewCells will load a different app detail into the detailsViewController. Figure 2–59 demonstrates a simulated result of this application.

Image

Figure 2–59. Resulting detail views in your simulated application

UITableViewCell Prototypes

The app is working as intended up to this point, but what if you add new apps to your inventory? With your current app layout, it would mean having to update your UITableView with new cells for each new app item. Wouldn’t it be easier if you loaded the UITableView dynamically so you didn’t have to update the storyboard XIB each time you had a new app?

First, you’ll create a custom UITableViewCell class that has outlets that model your existing UITableViewCells. Use the menu option File Image New Image New File…, and select “ Objective-C class” from the Cocoa Touch templates. Create a subclass of UITableViewCell, and name it “AppUITableViewCellClass”, as in Figure 2–60.

Image

Figure 2–60. Configuring a UITableViewCell subclass

In the interface file (.h), create two UILabel outlet properties and a UIImage outlet property. It should look like this:

//  AppUITableViewCellClass.h

#import <UIKit/UIKit.h>

@interface AppUITableViewCellClass : UITableViewCell

@property (strong, nonatomic) IBOutlet UILabel *labelAppName;
@property (strong, nonatomic) IBOutlet UIImageView *imageAppIcon;
@property (strong, nonatomic) IBOutlet UILabel *labelAppDescription;

@end

All that’s left to do is synthesize those properties in the implementation file (.m):

//  AppUITableViewCellClass.m

#import "AppUITableViewCellClass.h"

@implementation AppUITableViewCellClass

@synthesize labelAppName;
@synthesize imageAppIcon;
@synthesize labelAppDescription;

@end

Switch back to your storyboard view, and switch the UITableView content mode to Dynamic Prototypes using the Attributes inspector, just as in Figure 2–61.

Image

Figure 2–61. Reselecting Dynamic Prototypes

Your table view should now look something like Figure 2–62. You will notice that the segues you had created to the App Detail view are now gone, along with your static cell layouts.

Image

Figure 2–62. Your new table view

Drag the UIImageView and UILabel objects to the UITableViewCell prototype, along with their contents “App Name Label” and “App Description”, as shown in Figure 2–63. Then set the class on the UITableViewCell to your custom AppUITableViewCellClass in the Identity inspector pane.

Image

Figure 2–63. Configuring cells as a custom subclass of UITableViewCell

Now ^-click-drag from the UITableViewCell to the App Name label, and when you release the mouse button, the pop-up with the list of outlets should be displayed. Select the labelAppName outlet. Connect the other objects in the UITableViewCell. Figure 2–64 demonstrates the first of these steps.

Image

Figure 2–64. Connecting cell elements to outlets

You also need to set the Table View Cell Identifier field in the Attributes inspector, as shown in Figure 2–65.

Image

Figure 2–65. Setting the cell identifier

Now ^-click-drag from the UITableViewCell to the App Detail view controller to create the segue, as shown in Figure 2–66.

Image

Figure 2–66. Reconfiguring detail view segues

And set the identifier for the segue by selecting the segue and setting it in the Attributes inspector, as Figure 2–67 demonstrates.

Image

Figure 2–67. Setting the new segue identifier

Now you need to add the AppUITableViewCellClass and the datasource methods to the AppListTableViewController implementation file (.m).

//  AppListTableViewController.m

#import "AppListTableViewController.h"
#import "AppDetailsViewController.h"
#import "MyAppClass.h"
#import "AppUITableViewCellClass.h"

@implementation AppListTableViewController

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
    if([segue.identifier isEqualToString:@"AppDetailsLoadFromTableViewCell"]){
        AppDetailsViewController *appDetailsVC = segue.destinationViewController;
        appDetailsVC.selectedApp=[MyAppClass appWithAppID:[[self.tableView
indexPathForSelectedRow] row]+1];
    }
}

#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return 3;
}

- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    //Set the CellIdentifier that you set in the storyboard
    static NSString *CellIdentifier = @"appCell";

    AppUITableViewCellClass *cell = [tableView
dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[AppUITableViewCellClass alloc]
initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    }

    //Configure the cell
    MyAppClass *appForCell=[MyAppClass appWithAppID:indexPath.row+1];
    cell.labelAppName.text=appForCell.appName;
    cell.labelAppDescription.text=appForCell.appDescription;
    cell.imageAppIcon.image=appForCell.iconImage;

    return cell;
}

Now when you run the app, the app UITableView will load as before using the one prototype cell and the datasource. Figure 2–68 shows the simulated result of your newest updates to your application. In this instance, you are still using static data for the MyAppClass, but this app could easily be extended to use a core data object model or even pull a list of apps from a remote XML file on your server so that it is truly dynamic. Those features will be covered more in Chapters 10 (Data Storage Recipes) and 11 (Core Data Recipes).

Image

Figure 2–68. New application’s views after the latest changes

Adding a Storyboard to an Existing Project

You’ve created this “About Us” app, which works well, but it is pretty unimpressive on its own. It is meant to be included in your other apps so that you can easily show information about your company in any of your apps and also cross-promote your app library easily.

Apple has provided an API for storyboards so that they can easily be included in existing apps that may not leverage storyboards yet. The UIStoryboard class provides the method +storyboardWithName:bundle: that will load a storyboard with the given name. You can then load the initial view controller in the storyboard with the method – instantiateInitialViewController.

Let’s create a new project called “Chapter2Project” without storyboards. Use the menu option File Image New Image New Project… to create a new single view application. The window in which you do this will resemble Figure 2–69.

Image

Figure 2–69. Selecting a single view application

And this time, you will call the project “Chapter2Project” and make sure that Use Storyboard is not selected, just as in Figure 2–70. If your version of Xcode includes it, make sure the Use Automatic Reference Counting box is checked as well. If your version also includes a field for Class Prefix, set this to Chapter2Project.

Image

Figure 2–70. Configuring a new project without storyboards

Using the navigation pane of this new project, create a new group under the project named “AboutUsStoryBoard”, the result of which is shown in Figure 2–71.

Image

Figure 2–71. Adding a subgroup to the project

Now select Chapter2ProjectViewController.xib, and add a UIButton that will launch your About Us view when you touch it. Your view should resemble that shown in Figure 2–72.

Image

Figure 2–72. Configured view controller’s interface

Let’s go ahead and connect that About Us button to an IBAction by using the Assistant Editor and doing a ^-click-drag from the UIButton to the Chapter2ProjectViewController interface file (.h). Change the connection type to Action, and enter showAboutUsView for the Name, as shown in Figure 2–73.

Image

Figure 2–73. Configuring the UIButton’s action

Before you can complete that method placeholder, you need to copy your About Us files to this project. Switch to the About Us project, and, so you don’t get the About Us storyboard confused with any future storyboards you might add to projects, you should rename the storyboard. In the project navigator pane, change the name of MainStoryboard.storyboard to AboutUs.storyboard. If you have opted to create a universal app, this will initially be called MainStoryboard_iPhone.storyboard instead. In Figure 2–74, you can see the storyboard file renamed to the new name.

Image

Figure 2–74. Renaming the storyboard file for the new project

Now select all the files in the AboutUs group except the aboutUsAppDelegate.h/.m files, and copy them to the Chapter2Project project in the AboutUs group you created previously. If you move these files via “drag and drop,” make sure that the box marked “Copy items into destination group’s folder” is checked in the resulting transfer window. Figure 2–75 shows the resulting navigator pane with your files copied from your previous project.

Image

Figure 2–75. Copying storyboard files into your new project

Now you can complete that IBAction you created in the Chapter2ProjectViewController implementation file (.m). Select Chapter2ProjectViewController.m in the project navigator pane, and import the aboutUsViewController.h header file. Then scroll down to the bottom of the code where the IBAction method placeholder is, and complete it so the file looks like this:

//  Chapter2ProjectViewController.m

#import "Chapter2ProjectViewController.h"
#import "aboutUsViewController.h"

@implementation Chapter2ProjectViewController

- (IBAction)showAboutUsView:(id)sender {
    UIStoryboard *aboutUsStoryboard=[UIStoryboard storyboardWithName:@"AboutUs"
bundle:nil];
    aboutUsViewController *aboutUsVC=[aboutUsStoryboard
instantiateInitialViewController];
    [self presentViewController:aboutUsVC animated:YES completion:nil];
}
@end

When the project is run and the button is touched, your storyboard will load and all the subsequent views will load, resulting in a view resembling those shown in Figure 2–76.

Image

Figure 2–76. Resulting application with storyboard loaded

But there is a problem. There is no way to close your About Us view controller when you are done. The easiest method would be to add a Back button to the view in your storyboard that dismisses the view.

First you define the IBAction that your Back button is going to trigger in your aboutUsViewController interface file (.h):

//  aboutUsViewController.h

#import <UIKit/UIKit.h>

@interface aboutUsViewController : UIViewController

-(IBAction)closeAboutUs:(id)sender;

@end

Then implement the method in the aboutUsViewController implementation file (.m):

//  aboutUsViewController.m

#import "aboutUsViewController.h"

@implementation aboutUsViewController

-(IBAction)closeAboutUs:(id)sender{
    [self dismissViewControllerAnimated:YES completion:nil];
}

Open AboutUs.storyboard, and add a UIBarButtonItem object with a title of “Back” to the UINavigationBar in the About Us view. Then connect this UIBarButtonItem to the IBAction closeAboutUs by doing a ^-click-drag from the UIBarButtonItem to the View Controller status bar and then selecting the closeAboutUs event from the pop-up. These steps are demonstrated in Figure 2–77.

Image

Figure 2–77. Connecting the bar button to a segue action

Now when you run the application, you will be able to launch the About Us story board and get back to your main app. Don’t forget that you should add this UIBarButtonItem and method to the main About Us project so that when you copy it to future projects, you don’t run into the same issue.

Summary

The first thing you will notice in Xcode 4 is that Interface Builder is no longer a separate application. The changes, however, go much deeper and represent a truly integrated experience. Interface Builder has extended the ease of drag-and-drop interface building into code generation when combined with the Assistant Editor view.

The Storyboards tool takes your interface building even further and allows you to rapidly build working prototypes of your applications. Your storyboard diagrams are no longer thrown away when the coding starts but are an integral part of the application development process and can be utilized by controllers. This new feature takes the Model-View-Controller from an abstract best practice and makes it a tangible application development process.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset