Welcome to the second in our series of Beginning iOS Development tutorials. This tutorial will demonstrate how to build the FortuneCrunch application previously shown in our "Introduction to the iPhone SDK" post. In addition to being in a video format, this version has been updated for iOS4 and has much more detail on the various steps involved in crafting the application. Viewers can expect to become familiar with the Xcode and Interface Builder workflows, how to customize buttons and respond to button events, and fundamentals of Objective-C and Cocoa-Touch. A written transcript is also provided with this post.
Screencast Transcript
Introduction
Hello everyone, this is Mark Hammonds from Mobiletuts+, and welcome to the second post in our series on Beginning iOS Development. In today’s lesson, we’re going to build a simple iOS application called “FortuneCrunch.” I designed Fortune Crunch to be the “Hello World” of iPhone programming. It provides just enough detail for you to begin to understand the Xcode and Interface Builder workflow, the fundamentals of Objective-C syntax, and how to respond to button actions.
Step 1. Setting up an Xcode Project
Let’s begin by starting Xcode. Xcode is Apple’s proprietary Integrated Development Environment, and, as such, it includes all the tools you’ll need to start building native iPhone applications. Begin by selecting "Create a new Xcode project."
You’re now presented with a list of different iOS application templates. Each template is tailored for a different kind of application, and you can read more by the description provided when you click on a template icon. For our application, we are going to select the "View-based Application" template as we only need a single screen for this project. Enter the name "FortuneCrunch" and click save.
And welcome to Xcode. We will cover Xcode more fully in a future tutorial, but, for now, I really just want you to familiarize yourself with the three main content areas presented. The "Groups & Files" pane is a listing of all the files in your project. If you select a folder, you’ll see all the files within it displayed to the right, and, finally, selecting an individual file will display the contents in the pane beneath. This is also your code editor, which you can enlarge by either dragging the pane up or down or by double-clicking the file to open it in a new window.
Okay, that’s all pretty intuitive. Let’s take a closer look at the Groups & Files pane. The "Classes" folder is where almost all of the source files in your applications will be stored. As you might have guessed, this means that in Objective-C, you are naturally almost always editing class files because virtually everything in your application is an object.
By default, the "Other Sources" folder holds two very useful files: FortuneCrunch_Prefix.pch and main.m. The main function in main.m is called by the iOS operating system and is the first code to execute in your application. Although you’ll rarely use it, it is ground-zero for custom code execution. The FortuneCrunch_Prefix.pch is a good way to reduce the odds of developing carpel-tunnel syndrome, which is to say that it allows you to automatically import code from various libraries or header files across all of your project source files. The resources folder is generally used to organize all of your XIB files, plist files, images, audio, and video. The Frameworks folder displays all the frameworks currently available to your project, and the products folder displays the application binary. Note that it’s currently red, which means that it’s missing because we haven’t actually created a binary file yet.
Let’s solve that problem now. Make sure that your building for the simulator, rather than a physical device, and then go ahead and click the "Build and Run" icon to start the compilation process.
Welcome to the iPhone simulator. As you can see, this is our default project template in-action. Pretty boring, isn’t it? We’ll fix that next.
Step 2. Launching Interface Builder and Creating the interface
Switch back to Xcode and find the FortuneCrunchViewController XIB file. A XIB contains information about your view’s interface, and, consequently, is normally managed by Interface Builder. Double-click to open the file in Interface Builder now.
Looking at the middle of the screen, this window should look especially familiar. It’s the visual representation of the view being managed by the FortuneCrunchViewController class, and we just saw it when we compiled our default project. When working in Interface Builder, I like to think of this window as the "Canvas." Unlike an artist’s canvas, however, we can’t draw on the view directly, we can only places objects on top. That’s what the Library window is for. Go ahead and scan through the various objects listed here, and let’s even experiment a bit. We could place a Table View on the canvas, or perhaps an image, with a text-view underneath. But for our purposes, we’re going to use a button. And not just any button, but a class called "UIButton." Drag the button onto the screen now. As far as default buttons go, unfortunately, this one really couldn’t get much worse aesthetically. Be sure the button is selected, and then take a look at the inspector window. The Inspector window displays various options for customizing the objects on your view, and these options are quite literally the visual representation of your object’s properties, also referred to as data members. You’ll notice that one of the first options for a UIButton is the "Type" property. Let’s play around with this to see the various options.
We have "Detail Disclosure," "Info Light," "Info Dark," "Add Contact," and, our option of choice in this tutorial, custom. A custom button allows you to set the images for all of the button states available, which include the default state, the highlighted state, the selected state, and the disabled state.
For this tutorial, we are going to use UIButton to display a fortune cookie with the default state.To continue, we need to import our images.
To get the FortuneCrunch images provided with this tutorial, you’ll need to download the FortuneCrunch project code attached to this post. After downloading and unzipping the code, open up the FortuneCrunch folder, and you will see the two images we’ll need in your app -cookie-closed dot ping, and cookie-crunched dot ping. Select both of these images, and then drag them into the Resources folder in Xcode. Pay special attention to this dialogue box, there’s an option at the top, "Copy items into desination group’s folder" that you’ll need to check. As you might have guessed, this tells Xcode that you want to actually copy and import the images into Xcode instead of just linking in the files. Click add, and you should now have cookie-closed and cookie-crunched in your project. Before we continue, take a look back a the FortuneCrunch folder. You’ll also want to drag in the "Icon.png" file. By default, whatever image is titled "Icon.png" will become the application icon displayed by iOS. Notice though that our icon.png doesn’t have any gloss applied -this is applied automatically by iOS.
Now, flip back to Interface Builder. Select the UIButton object and change the image for the default state to cookie-crunched.png. Next select Layout -> Size to Fit. Reposition the button the way you’d like it to appear. We’re now going to add a UILabel to the canvas to represent our fortune. For my app, I’m going to go with the text "Happy iPhone Hacking!" Customize the text with the inspector window’s font option. I’m going to change my font to 12pt with a bold typeface. One thing to note about the font display, all the fonts on your system will be presented, but the majority of them aren’t available on iOS by default, so be careful when selecting a font style. Resize the label width with the Layout -> Size to Fit option, and then drag it to where you’d like it on the fortune paper. I’m also going to change the color of the text from black to red. Finally, set the "hidden" property to true, because we only want this label to appear after the user taps the cookie image.
We actually only set the default state of our UIButton to cookie-crunched so we could correctly position the label. With that done, we want our button’s default state to be the cooke-closed image. However, both the highlighted state and the selected state will be the cookie-crunched image. As you can see Interface Builder won’t reflect the properties set for these states; you’ll have to test those with code and the iPhone Simulator.
Our cookie-closed image has a white background, but the view background is gray. Change the view background to white to match the cookie images. There, much better.
Now, go ahead and save your work in Interface Builder and then switch back to Xcode. "Build and Run" the application again in Xcode to look at your progress.
Not quite what you were expecting, right? With the introduction of iOS 4, applications support multi-tasking by default. So when we pressed the home button before to close out the application, we didn’t actually stop the process, we just sent it to the background. One way to get around this fact is to double click the home button, hold down the cursor over our app icon, and then manually terminate the app process. However, a better way is to use "Build and Debug" instead of "Build and Run" when actively developing your app. And now you can see our changes.
Before we move on to actually coding our application, I’m going to give you a Pro Tip concerning Interface Builder files. Even though you will almost always edit XIB file’s with Interface Builder, a XIB file is really just an XML file that contains meta-data about objects that will be created automatically when the view controller is instantiated. You can see this by command clicking FortuneCrunchViewController.xib and then selecting Open As -> Source Code File. If you’re using a version control system like SVN, CVS, or GIT, you’ll likely also notice this when examining diffs between check ins.
Step 3. Coding the FortuneCrunchViewController Header File
Now, let’s move on to actually programming our app. Take a look at FortuneCrunchViewController.h and FortuneCrunchViewController.m. These two files combined comprise the view controller object that will control the single view in our application. The .h file is referred to as the class declaration, or interface file, or even just the "header" file. The .m file is referred to as either the class definition or the implementation file. The class declaration file is used to describe what our class is, whereas the class definition file is used to describe how our class actually works.
Of course, on their own, both are simply text files. It’s what’s inside that counts. Delete everything that currently exists in the interface file; we’re going to start from scratch.
The first several lines contained comments that looked something like this:
// comment line 1 // comment line 2 // comment line 3
Well and good, but Objective-C also supports another comment style:
/* Comment line 1 comment line 2 comment line 3 comment line 4 */
I personally prefer to use the multi-line comment style whenever possible, but we’ll stick with the single line comments to reproduce the original file.
The next line is an import statement. The #import
statement is a preprocessor directive that makes whatever file or framework we specify available to our class. It’s as though you had typed in the content of that file or framework directly at this point in the source file. Since we’re reproducing the original class definition file, we’ll say #import <UIKit/UIKit.h>
to add the UIKit framework. This framework contains many of the classes we will be using that are unique to iOS, such as UIButton, UIImage, UIViewController, etc.
Our next step is to declare that this file is a class declaration file with the @interface
statement. As you can see from the auto-complete feature, the syntax for declaring a class interface is @interface, class name, colon, superclass name, opening curly bracket, internal variables or ivars, sometimes called data members, closing curly bracket followed by method declarations, and then an @end
statement.
We’ll call our class FortuneCrunchViewController
and list the superclass as UIViewController as we want to inherit all the functionality from a default ViewController and just customize the parts unique to our view. We’re going to need two ivars in our class, one for the UIButton
with our cookie image, and one for the UILabel
that contains our fortune cookie text. Declare those variables like this:
IBOutlet UIButton *cookieButton; IBOutlet UILabel *fortuneLabel;
All that is really needed to declare either ivar is the class name, asterisk, variable name syntax. The IBOutlet
keyword stands for Interface Builder outlet, and is used as a tag that will allow us to make connections to this variable directly in Interface Builder.
We’re now going to declare that we want both of the above variables to be considered properties of the class. Do so with the following lines:
@property (nonatomic, retain) IBOutlet UIButton *cookieButton; @property (nonatomic, retain) IBOutlet UILabel *fortuneLabel;
Before we move on, let’s add the method we’ll use in this app to change the image of the cookie when tapped:
-(IBAction)crunchCookie;
The basic syntax for method declarations in Objective-C is a plus or minus sign, a return type, and a method name. In our case, we’re going to use a minus because this is an instance method. If we wanted this method to be a part of the class, we would use the plus sign to create a class method. Next, we specify our return type as IBAction, which is actually a synonym for void, the only difference being that IBActions, or Interface Builder actions, can be manipulated in Interface Builder.
Other possible return types include primitive types like int, char, and float, or classes like NSString, UIImage, and NSNumber. Appropriately enough, we name our class "crunchCookie" because that is precisely what it will do.
Finally, we need to add the @end
statement underneath our method to end our @interface
declaration.
Now, save your work in Xcode and switch back to Interface Builder.
Step 4. Connect Interface Builder to FortuneCrunchViewController
Having declared a label and a button in Xcode, we need to sync the objects we’ve placed onto the view in IB with the ones in our declaration file.
Take a look at the file’s owner object in the XIB contents window. If you look at the inspector for this object, you’ll notice that File’s Owner is actually an instance of FortuneCrunchViewController.h. This is the representation of the class declaration we were just editing, and if you right click, you’ll notice that our IBOutlet variables are listed under the outlets section. We can now connect our button, and our label.
We also need to connect our button action to the method we declared. Select the button on the canvas and right click. You’ll see this long list of button events. The two most common are touch up inside and touch down. Touch up inside is fired when a user touches down on the button, and then releases the button with their finger still inside the button bounds. So, the event wouldn’t fire until the user actually takes their finger off the button. In our case, we want the image to change as soon as possible, so we want to use the touch down event, which will fire as soon as a touch is detected on the button. Drag from the touch down circle to the File’s Owner object to sync with our crunchCookie method.
That’s it. We now have our IBOutlets and IBAction synced with the objects in our XIB file. Save your work and switch back to Xcode.
Step 5. Code the FortuneCrunchViewController Implementation File
We’re finally ready to begin coding our class definition file. Open FortuneCrunchViewController.m. As we did before, delete everything so we can start from scratch.
We’ll begin the file with some standard boilerplate comments:
// // FortuneCrunchViewController.m // FortuneCrunch //
The first thing we want to do is to import the declaration file, or header file, that we coded previously. Do that with the import statement:
#import "FortuneCrunchViewController.h"
You might be wondering why we need to import the header file here. Why not just type it in directly and keep everything in one file? In fact, you could do this, but, by convention, class declarations and class definitions are usually kept in two separate files.
Next, we need to declare our implementation. This is done with the @implementation
statement:
@implementation FortuneCrunchViewController
This is quite straightforward. I’ll go ahead and add the command to end our implementation as well, so I don’t forget to do so later:
@end
In our declaration file, we stated that we wanted both fortuneLabel and cookieButton to be properties of this class. We’re going to finish the job now by synthesizing those properties, like so:
@synthesize cookieButton; @synthesize fortuneLabel;
When a property is synthesized, the methods responsible for storing and retrieving the data stored in those properties will be automatically created for us when the application is compiled. This means that we will be able to assign and retrieve values for the properties without typing out the boiler-plate methods necessary to do so.
Now, let’s move on to adding the actual crunchCookie method:
-(IBAction)crunchCookie { [cookieButton setImage:[UIImage imageNamed:@"cookie-crunched.png"] forState:UIControlStateNormal]; fortuneLabel.hidden = FALSE; }
That’s it. On lines 3-4, we’re calling the setImage:forState method of the cookieButton object, and passing in a new UIImage and the UIControlStateNormal constant. This probably looks pretty foreign to you if you’re coming from another programming language. Don’t worry too much about the syntax in this tutorial, all will be revealed in a future tut. The next line is more straightforward: we’re changing the value of the fortuneLabel object’s “hidden” property. Remember checking the “hidden” box in Interface Builder? That set this property to true and made the label hidden. We change that by setting it to FALSE here so our fortune text will be displayed when the cookie crumbles.
We’re almost ready to build and debug our changes, but there are a few more methods that we need to add first:
-(void)viewDidUnload { } -(void)dealloc { }
These are inherited methods from the UIViewController class that deal with memory management. The viewDidUnload
method is called when the device is low on memory and needs to free unused views. In this scenario, you need to release any view related objects that will be re-loaded when the view is instantiated again. These elements are usually tagged with the IBOutlet keyword, and in our case that means the fortuneLabel and cookieButton objects. Release them like so:
-(void)viewDidUnload { [cookieButton release]; cookieButton = nil; [fortuneLabel release]; fortuneLabel = nil; }
The dealloc
method is called when the actual FortuneCrunchViewController object is about to be deallocated, or removed, from memory. You’ll want to release the same objects here as well:
-(void)dealloc { [cookieButton release]; [fortuneLabel release]; [super dealloc]; }
Again, don’t worry too much about the syntax for now. We’ll dive more deeply into memory management in a future tutorial.
Conclusion
Okay, ready to see the fruit of your hard labor? Save the file and hit build and debug. When you tap the cookieButton, you’ll see your fortune revealed.
Congratulations on completing the second tutorial in our series on iOS development. iPhone development with Objective-C has a steep learning curve, but hopefully after completing this tutorial you are more comfortable working with Xcode and Interface Builder, and are beginning to understand some of the fundamentals of application development with Cocoa-Touch. If you have any questions, feel free to leave them in the comments section below or to contact me directly via Twitter: @markhammonds. Until next time, Happy iPhone Hacking.
Comments