"

Posts Tagged ‘Interface Builder’

In a previous post, I show how to build a navigation-based iPhone app using Interface Builder starting from a window-based project. Here, I repeat it for a tabbar-based application.

As in the previous case, the idea is to build a simple tab bar application with only 3 tabs. Each tab is going to be associated to a view controller.

So, launch Xcode and create a new window-based iPhone project (make sure that the option “Use Core Data for storage” is unchecked). Name the project MyTabBar. Edit the MyTabBarDelegate.h file in the following way

1
2
3
4
5
6
7
8
9
10
11
12
#import <UIKit/UIKit.h>

@interface MyTabBarAppDelegate : NSObject <UIApplicationDelegate>
{
    UIWindow *window;
    UITabBarController *tabBarController;   // 1
}

@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet UITabBarController *tabBarController;  //2

@end

In line 1, I declare a new outlet tabBarController that will be connected in Interface Builder with a UITabBarController object I will add later. Then, I create a new property (line 2) for the added outlet. Now, save this file and switch to the class implementation (MyTabBarDelegate.m). Edit this file in the following way

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#import "MyTabBarAppDelegate.h"

@implementation MyTabBarAppDelegate

@synthesize window;
@synthesize tabBarController; // 1

- (void)applicationDidFinishLaunching:(UIApplication *)application
{    
    [window addSubview:tabBarController.view]; // 2
    [window makeKeyAndVisible];
}

- (void)dealloc
{
    [tabBarController release]; // 3
    [window release];
    [super dealloc];
}

@end

First, in line 1, I synthesize the previously added property. Then, I release the property in the dealloc method (line 3), because of its retain attribute and finally, I add the tabBrController’s view to the main window (line 2). Save this file.

Now, open the MainWindow.xib file. Drag from the library a UITabBarController object and drop it to the MainWindow.xib window. You should end up with this:

Figure0

Then, connect the My Tab Bar App Delegate tabBarController outlet to the Tab Bar Controller object (right-click the My Tab Bar Delegate and drag a line from the tabBarController outlet to the Tab Bar Controller object). This is shown in the following figure.

Figure1

Double-clicking the Tab Bar Controller object, a new window as the following appears:

Figure2

Since we want to have 3 view controllers, we need to add a new tab. Hence, select a TabBarItem (UITabBarItem) from the IB Library and drag and drop it to the Tab Bar Controller window in correspondence of the tab bar. After doing that you should have 3 tabs in the bar, as in the following figure:

Figure3

Before going on, I want to point out something important. Select the Tab Bar Controller object in the MainWindow.xib window. Then, open the Inspector and select the Attributes panel. There, you find the list of view controllers associated for each tab with their respective titles and classes. Click on one of the view controller class. In the pop-up menu, you find the different view controller types you can have for each tab. So, if you need a navigation controller, you need to define it here. Then, you can build a navigation controller stack as we did last time.

Another important things is the following. If you click any of the tab of the previous figure, the Inspector shows the view controller properties. If you click again on the same tab, you get the tab properties. Here, you can change the tab title, the icon, and much more. If you click between 2 tabs, the full bar is selected and you can change its properties.

Now, save and quit IB. Go back to Xcode and Build & Run. You get a white window with a tab bar. Nothing interesting. Now, we need to add the 3 view controllers.

I do it for just one of them. You simply repeat these steps for the other two view controllers.

  1. In the Groups & Files, right-click on the Classes folder and choose Add -> New Files…
  2. In the Cocoa Touch Class group, select the UIViewController subclass. Check the “With XIB for user interface” and uncheck “UITableViewController subclass”. Press Next.
  3. Name it “FirstViewController” (you will name the other view controllers as SecondViewController and ThirdViewController). Be sure that “Also create FirstViewController” is checked. Press Finish.
  4. Open the FirstViewController.xib and change the background color of View window to any color (to make it easier, we do this to distinguish the 3 view controllers).

Repeat the previous step and assign a different color for the second and the third view controllers.

Done that, we need to associate each tab to a view controller. To do so, open the MainWindow.xib file and click on the first tab (to make sure that you are selecting the view controller, after clicking on the tab, click inside the window). In the Inspector, select the Attributes pane and choose as NIB Name “FirstViewController”. Then, go to the Identity Inspector and choose the FirstViewController as class name. Repeat these steps for the second and third view controllers.

When done, save and quit IB. Finally, build and run.

I attach the project here.

Keep coding,

Geppy

Post to Twitter Post to Facebook

During the different SDK releases, Apple has changed many things. Now, developers can build their own applications using different approaches, but I personally believe that an intensive use of Interface Builder is a good strategy to set up in few minutes a project and easily maintain it.

During my trainings, I collected the request of my students and most of the time, they wish to use more and more Interface Builder, because it is more intuitive and easier to use than simply typing code.

In this post, I want to explain how to build the infrastructure for the UINavigationController using Interface Builder. I think this is the best, cleanest and quickest way to build an app. If you just repeat this exercise a couple of times to understand the concept, you will be able to build a navigation controller-based app in few minutes.

To better understand what we do, I will start from an window-based project without taking advantage of the view-based template prepared by Apple. So, open Xcode and create a new project. Select a Window-based Application template and name it MyNav. We could have chosen the View-based Application template and Xcode would have setup the whole infrastructure for us, but then you loose all the magic behind it. So, I prefer you build it from the scratch, so that you will learn how it works and you can easily modify it to create more complex functionalities.

Give a look at the created project. It contains three files: MyNavAppDelegate.h, MyNavAppDelegate.m and MainWindow.xib. We need to modify these three files to go on, but let us start with some design.

Suppose you want to navigate 3 views: view1, view2 and view3. The view1 is the view appearing as soon as the app finishes launching. This is called the main view. Since it is the first view in the sequence, view1 is also called the root view. Root view and main View do not always coincide. For example, in the Contacts app, the main view is the All Contacts list, while the Root View is the Groups view.

Each view of our project will be a UIViewController and the Navigation Controller will be responsible to push or pop the next or the previous view onto the screen as they were in a stack. So, let us start.

Modify the MyNavAppDelegate.h file, so that it looks as follows:

1
2
3
4
5
6
7
8
9
10
11
#import <UIKit/UIKit.h>

@interface MyNavAppDelegate : NSObject
{
 IBOutlet UIWindow *window;
 IBOutlet UINavigationController *myNavController;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet UINavigationController *myNavController;

@end

Now, let us modify the implementation file. Edit the MyNavAppDelegate.m file like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#import "MyNavAppDelegate.h"

@implementation MyNavAppDelegate
@synthesize window;
@synthesize myNavController;

- (void)applicationDidFinishLaunching:(UIApplication *)application
{
 [window addSubview:myNavController.view];
 [window makeKeyAndVisible];
}
- (void)dealloc
{
 [myNavController release];
 [window release];
 [super dealloc];
}
@end

Now, fire up Interface Builder, double-clicking the MainWindow.xib file. From the Library drag a Navigation Controller and drop it in the MainWindow.xib.

Control-click the My Nav App Delegate object in the MainWindow.xib window and, when the HUD window opens, connect the myNavController outlet with the recently added Navigation Controller.
So, what have we done in the last part? It is very simple: we declared in the @interface of the MyNavAppDelegate a UINavigationController outlet. In the @implementation, we should have allocated and initialized this UINavigationController, but to avoid this, we added a Navigation Controller object with Interface Builder to our project. In this way, the XIB file contains the Navigation Controller. When we launch the app, the MainWindow.xib file, containing the Navigation Controller, is automatically loaded. Since this is a generic object to let Xcode understand that we are referring to the same Navigation Controller defined in the @interface, we need to connect the outlet of the App Delegate to the UINavigationController.

Now, we need to add the 3 UIViewController(s) to manage each view in the stack. The use of the UIViewController instead of simple UIView is requested by the fact that a UIViewController is a controller and thus, it is able to manage complex functionalities, such as screen rotations, that maybe you will use later.

The approach to setup a UIViewController and its XIB file is the same for each view, so I show you how to do it only for one of them. Then, you simply repeat the following steps:

  • Right-click the Classes group in the project window of Xcode and select Add->New File…
  • Select the UIViewController subclass template from the Cocoa Touch Classes and click Next.
  • Name the view FirstViewController (make sure that the Also Created flag is selected) and click Finish. You get two files (FirstViewController.h and FirstViewController.m).
  • Right-click again the Classes group and choose Add -> New File… This time, select View XIB template from the User Interface under iPhone.
  • Click Next and name the file FirstViewController.xib.
  • Click Finish.

Repeat the last paragraph for the second and third view (name them SeconViewController and ThirdViewController).

When you have the three controllers, double click the FirstViewController.xib. The MainWindow.xib window contains 3 objects: File’s Owner, First Responder and View. The File’s Owner is a Proxy Object: it simply represents in IB an object that you have created somewhere else (coding or using IB). We need to specify which object is exactly. To do this, select the File’s Owner and the Identity pane in the Interface Builder Inspector (Tools -> Identity Inspector). Choose as Class name FirstViewController. Repeat this step for the SecondViewController and for the ThirdViewController. When done, you have clearly associated each class to its XIB file.

Now, our File’s Owner represents the FirstViewController object. Right-click it and connect the view outlet with the View object. Open the View object (double-click it) and select the Attributes pane in the Inspector (Tools -> Attributes Inspector). Click on the Background color picker and change the color to red, so that we can distinguish it from the other views we are going to set up.
Now, we have to tell the Navigation Controller that the FirstViewController is the first view we want to load (the Main View, remember?) and use it as root view. Here, there is a small trick. Open again the MainWindow.xib file and double click the Navigation Controller. Click in the window of the Navigation Controller and select the Identity Inspector.
Select as class name FirstViewController. Then, select the Attributes Pane and select FirstViewController as NIB Name. The Navigation Controller should show now a link to the FirstViewController.xib as shown here on the right-hand side. Before you can Build and Run, choose the Detailed View of the MainWindow.xib. Click on the triangle close to the UINavigationController. Inside it, there is UIViewController. You need to make sure that this is is the right one. Select it and set its class name to FirstViewController in the Identity Inspector. In the same way, set to FirstViewController, the XIB Name to FirstViewController in the Attributes Inspector. If you miss these steps, your app will not work. It should look like in the following picture.

You can now Build and Run. You will get a window with a NavigationBar and red view. Let’s go back to the project.
So, we have set our root view. Now, we have to inform each view about the next view of the desired view sequence. You will see that many functionalities are for free and the code we are going to write is represented by few lines.
Edit the FirstViewController.h so that it looks like this:

1
2
3
4
5
6
7
8
9
10
11
#import <UIKit/UIKit.h>

@class SecondViewController;

@interface FirstViewController : UIViewController
{
 IBOutlet SecondViewController *mySecondViewController;
}
@property (nonatomic, retain) IBOutlet SecondViewController *mySecondViewController;

@end

Remember to synthesize the property and add the following header #import “SecondViewController.h” in the FirstViewController.m file.

Now, open the FirstViewController.xib and add a UIViewController to the MainWindow.xib FirstViewController.xib window. Select this view controller and set its class name to SecondViewController in the Identity Inspector. Then, set also to SecondViewController the XIB Name in the Attributes Inspector. Now, finally, connect the File’s Owner outlet mySecondViewController to the view controller.

We do the same for the second view controller. Before that, open the SecondViewController.xib file and add a UIViewController object to the MainWindow.xib SecondViewController.xib window. Select this view controller and set its class to the ThirdViewController in the Identity Inspector and set its XIB Name to ThirdViewController in the Attributes Inspector.
Now, open the SecondViewController.h file and modify it as follows:

1
2
3
4
5
6
7
8
9
10
#import <UIKit/UIKit.h>

@class ThirdViewController;

@interface SecondViewController : UIViewController
{
 IBOutlet ThirdViewController *myThirdViewController;
}
@property (nonatomic, retain) IBOutlet ThirdViewController *myThirdViewController;
@end

Remember to synthesize the property and add the following header #import “ThirdViewController.h” in the SecondViewController.m file.

Now, build and run it. You don’t see any difference with the previous run. The infrastructure is already set up, but we need to add some way to push the next view to the screen. We can do it in different way. We choose the simplest one, using a push button in the center of the current view.
Add a button to the first and second view controllers. For each of them, add the following action in the header file of the corresponding class:

1
- (IBAction)bringNextView:(id)sender;

In the implementation, add the following:

1
2
3
4
- (IBAction)bringNextView:(id)sender
{
 [[self navigationController] pushViewController:mySecondViewController animated:YES];
}

Repeat the previous code for the second view controller. Don’t forget to connect the button to the IBAction of the File’s Owner. I attach the project MyNav.zip. I only add small things to make it to like nicer. If you have any question, simply contact me.

Have a nice coding!!!

Post to Twitter Post to Facebook

Undo and Redo

Wednesday, June 17, 2009 @ 09:06 AM
posted by Geppy

This topic is quite difficult to digest, but after few exercises you should be able to manage it.
When you work with an application, it usually happens that, after applying some edits to your data, you realize that the final result is not what you were expecting. So, you wish to go back and remove what you have done. I believe you are familiar with the Undo and Redo items in the Edit menu of your Mac. But, how can you implement these functionalities in your application?
Cocoa and Cocoa Touch use an intelligent approach to manage this. To understand it, I have to introduce the concept of NSInvocation. An NSInvocation is an object containing all the elements of an Objective-C message: a target, a selector, some arguments, and the return value. Each of these elements can be set directly and the return value is set automatically when the NSInvocation object is dispatched. NSInvocation works together with the NSUndoManager to make the Undo/Redo mechanism possible.
When you apply an edit onto your data and you want to be able to undo that edit, you need to prepare (coding) the inverse operation corresponding to that particular edit. Then, you pack this inverse message (including selector, arguments and return value) in an NSInvocation object and forward it to the NSUndoManager. This will store this object in a stack and wait for the user action.
Indeed, there are two stacks: the Undo Stack and the Redo Stack and their scope is very similar. The management of the two stacks is assigned to NSUndoManager and you don’t need to take care of it. When you undo some operation, NSUndoManager removes from the Undo Stack that operation and adds it to the Redo Stack. If you redo that operation, this time the NSUndoManager removes it from the Redo Stack and adds it to the Undo Stack.
Let’s look at the details and how to code it. Suppose you are writing a simple calculator with a plus and minus operations:

1
2
- (NSNumber *)add:(NSNumber *)aNumber to:(NSNumber *)anotherNumber;
- (NSNumber *)subtract:(NSNumber *)aNumber from:(NSNumber *)anotherNumber;

You want to be able to undo and redo each of these operations after you apply them to your data. When you start your application, an NSUndoManager object is created for you with no costs. In reality, if you are writing a document-based application in Cocoa, an NSUndoManager is created automatically. In the other cases (iPhone included), you have to create it explicitly.
You usually need only an NSUndoManager for each application. To register the above two methods to the NSUndoManager mechanism, you do the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- (NSNumber *)add:(NSNumber *)aNumber to:(NSNumber *)anotherNumber
{
    NSUndoManager *undo = [self undoManager];
    [[undo prepareWithInvocation] subtract:aNumber from:anothernumber];

    float F1 = [aNumber floatValue];
    float F2 = [anotherNumber floatValue];
    F1 += F2;
    return [NSNumber numberWithFloat];
}

- (NSNumber *)subtract:(NSNumber *)aNumber from:(NSNumber *)anotherNumber
{
    NSUndoManager *undo = [self undoManager];
    [[undo prepareWithInvocation] add:aNumber to:anotherNumber];

    float F1 = [aNumber floatValue];
    float F2 = [anotherNumber floatValue];
    F1 -= F2;
    return [NSNumber numberWithFloat];
}

The undoManger method (lines 3 and 4) is defined in the NSDocument, NSResponder, NSManagedObjectContext and WebView classes. Please, refer to the documentation of these classes for details.
The trick is at the lines 4 and 15. There, you register to the Undo/Redo mechanism the opposite operation to your current edit.
You can also change the label of the Undo and Redo operation using the

1
- (void)setActionName:(NSString *)actionName;

So, you can add this line after line 4:

1
[undo setActionName:@"Add"];

Once implemented, you will have in the edit menu “Undo Add”.

Post to Twitter Post to Facebook