Sunday, April 5, 2009

Tutorial: Understanding the iPhone Interface Builder (IB)

In november 2008, I decided to start exploring the Apple iPhone development environment. The main reason for doing that was that I wanted to port a mobile game I wrote for the Mophun platform five years ago. Another reason was that I thought the iPhone seemed like an exciting platform.

Before starting to code I took the time to read quite a lot of official documentation from Apple. The primary problem with the Apple iPhone documentation in my view is that there is so much of it that it becomes hard to choose what to read. A secondary, but quite important, problem is that a lot of the documentation contains the wrong information, at least for someone who isn't familiar with Mac development. That the documents contain a lot of words, but little information is also quite frustrating. Thankfully, the API documentation available in XCode is much better.

However, I managed to find a handful of good documents and after reading them I thought I was well prepared for iPhone development. At this time there was almost no tutorials, blogs or discussion forums on the Internet since the Apple NDA had just been lifted. Therefore I started to explore on my own and got a complete chock when I started up the Interface Builder (IB). I didn't understand anything, which made me feel like a complete fool. Especially after spending so much time reading documents.

I quickly gave up the IB and started doing things programmatically instead, that is, doing everything by hand in source code; creating windows, views, buttons, etc. This turned out to be really simple so my self confidence was (partly) restored. It also made me feel that the problem wasn't my lack of understanding, but the lack of good documentation for the IB - see what a little self confidence can do ;)

A few days ago, I decided to return to IB and make a new attempt at understanding it. This time it went a little better since I had gained a lot of real iPhone experience since the last attempt. To force myself to really understand it, I decided to start by trying to fully explain the code and xib-files created by the XCode project templates.

Create the project.
  • Start XCode or choose "File/New project" from the menu-bar if XCode already is running.
  • In the left column, select: "iPhone OS/Application".
  • In the right box, select: "Window-Based application".
  • In the right bottom of the window, click the "Choose"-button.
  • Rename the project to "Test1" (that's the figure one, not the letter ell) by replacing the text "Untitled" in the "Save as"-box and save the project an appropriate place and press the "Save"-button.
After completing these steps, XCode will create a set of files for you which are displayed in the left column of the window using an expandable tree-view. Below is the hierarchy of files we will concentrate on in this post:

Test 1
  Classes
    Test1AppDelegate.h
    Test1AppDelegate.m
  Resources
    MainWindow.xib
    Info.plist

Explanation of the files

Classes/Test1AppDelegate.h

This is the h-file (specification) for the class in our application which will handle events/messages from the UIApplication-object which exist in all iPhone applications. Let's look at the most import lines of code.

Test1AppDelegate : NSObject <UIApplicationDelegate> {
Specifies that this object inherits from NSObject and implements the UIApplicationDelegate protocol. (A protocol is similar to an interface in Java.)

UIWindow *window;
The object will make use of an UIWindow which is the "base GUI component" in an iPhone and container for all other GUI components. Therefore we create a pointer to our window, which we will call 'window'.

@property (nonatomic, retain) IBOutlet UIWindow *window;
Properties are more or less instructions to the compiler to generate accessor methods (getters and setters) for a property (member field/variable) of the object. Here we want our variable 'window' to be accessible to other objects, so the compiler will create declarations for it here in the h-file. We also flag the property with 'IBOutlet' so that we can manipulate the it using IB.

Classes/Test1AppDelegate.m

This is the m-file (implementation) of the class Test1AppDelegate.

synthesize window
Instructs the compiler to create the actual accessor method (set/get) definitions (implementation), according to the instructions in the @property declaration in the h-file.

- (void)applicationDidFinishLaunching:(UIApplication *)application {
This method is part of the UIApplicationDelegate protocol and gets called when the application has finished launching(!).

[window makeKeyAndVisible]
This line calls the method makeKeyAndVisible in our window 'window'. What might seem strange here is that we so far hasn't seen any code for creating the window, just a declaration of the window in the h-file. This is because we don't create this window programmatically (in source code), but instead use IB to create, but more on that later.

Resources/MainWindow.xib

This is an IB-file, which was created automatically by XCode when we chose to create a "Window-Based Application" project. We will return to this file soon.

Resources/Info.plist

This is a "property list" which lists same basic properties for the application we are creating. Click it to see its contents. The most important property is the "Main nib file base name" which is set to "MainWindow", meaning that it points to the file Resources/MainWindow.xib in our project. The xib-file listed in the property will be automatically loaded by the iPhone when the application is started.

A first test run

In order to see some action, lets try to build and run our application to see what it does. In XCode, choose "Build/Build and Go" from the menu or simply press CMD-Return. This should bring up the iPhone simulator and display a completely white screen. Not too exciting, but quite impressive considering we still haven't written a single line of code ourselves. Press the Home-button on the iPhone simulator and navigate to the last page of icons on the home screen and you should see an icon called 'Test1' - yes, that's our little application!

Interface Builder (IB)

Ok, now it's time to start exploring the IB. First of all, the most important thing to understand about the IB is that it creates actual Obejctive-C objects and not source code! It can also set the properties of the objects, for example to "connect" two objects two eachother (by setting a pointer in object A to point to object B). The created objects, including the values of their properties, are then saved into a xib-file which can be loaded by the application. When the file is loaded the objects are re-created by the application in run-time. (For Java-coders, this is similar to reading a file containing serialised objects.)

To start IB, double-click Resources/MainWindow.xib. This will start IB and bring up some windows, where the window titled "MainWindow.xib" is the starting point. This window displays the content of a xib-file, in this case our xib-file called MainWindow.xib.

File's owner.

This object represents the object which eventually will load this xib-file - the object which "owns" this xib-file - and that is why it has the very confusing name "File's owner".

In our case, the real object owning this xib-file is the UIApplication automatically created by the iPhone when our application is started. The UIApplication object knows it should load this xib-file since we specified it in the Info.plist file.

The most important tool in IB is the "Inspector window" which can be invoked from the "Tools/Inspector" menu. It has four "tabs" called "Attributes", "Connections", "Size" and "Identity", which can be reached directly from anywhere in IB by clicking on an object icon and then pressing CMD-1 to CMD-4 respectively. In my view, "Identity" (CMD-4) is the best place to start since it tells us what kind of object we are dealing with.

So, click on "File's owner" and press CMD-4. Here we can see that this object is of the class UIApplication. We can also see that this is where the strange name "File's owner" comes from. The most interesting thing we see, though, is that it has a property (or outlet as it is called here) called 'delegate' which has the Objective-C type 'id'. As you probably know, 'id' is an "object pointer", which means that this property should be set to another object. In this case it should be set to an object which implements the UIApplicationDelegate protocol.

To see the value of the 'delegate' property, press CMD-2 to invoke the "Connections"-tab. Here we can see that 'delegate' is set to 'Test 1 App Delegate', which might sound familiar to you if you looked at the other objects in the IB window 'MainWindow.xib' which displays the contents of the xib-file. Thus, it seems as if there exists a connection between the objects "File's owner" and "Test1 App Delegate" which both exist in our xib-file. Please note that the names "File's owner" and "Test1 App Delegate" are IB-names, not the real names of the objects. To see the real names of the objects, or actually the class of the objects, select an object and press CMD-4.

First Responder.

I haven't fully understood the role of this yet, so I will try to explain it later...

Test1 App Delegate.

Ok, let's check the "Identity Inspector" for this one by pressing CMD-4. We see that this object is of the class Test1AppDelegate and that it has an outlet called 'window'. That's the class we defined in Classes/Test1AppDelegate.h and Classes/Test1AppDelegate.m! As you can see, our UIWindow-property 'window' also showed up, thanks to the IBOutlet in Classes/Test1AppDelegate.h

If we check the object's connections by pressing CMD-2, we see that our 'window' property is set to "Window". This means that a UIWindow with an IB-name of "Window" has been created in IB and connected to our 'window' property. If we check the contents of the xib-file displayed in the MainWindow.xib window we see that there actually is an object there called Window. We can also see which objects reference this object, that is, the "reverse connections". In this case we can see that the "File's owner" object has a connection to this object as we explained above.

Window.

Now, we have finally reached a GUI-object! Select the Window icon and press CMD-4 right away. We see that the class of this object is UIWindow. Press CMD-2 to check the connections: apparently there exists a "reverse connection" from an object with the IB-name "Test1 App Delegate". This means that "Test1 App Delegate" uses this Window.

For an object of UIWindow class, the Attributes (CMD-1) and Size (CMD-3) tabs also contain information. Pressing CMD-1 we can see that the background colour has been set to white, so finally we find the explanation for why we saw a completely white display when we test run our application previously! Pressing CMD-3 we can see that the size is set to 320x480 which completely fills the iPhone's display, which always should be the case for windows.

Finally, we have now explained the contents of the xib-file! Do remember that when the xib-file is loaded, the Objective-C objects it contains are created and the properties set in IB are set in the created objects as well. This is the explanation for how so little source code can accomplish so much.

No comments:

Post a Comment