Wednesday, July 18, 2012

How To Create A Simple iPhone App on iOS 5 Tutorial: Part 1/3


The iPhone is an amazing platform to develop on for indie software developers. It’s never been easier to come up with your own unique app idea, code something up, and have it be available to millions of potential customers!
Lately I’ve been getting a lot of questions from people new to iOS development asking how to get started. So I thought it would be helpful to write a tutorial series tailored for beginners.
But rather than focusing in detail on just one topic, we’re going to dive in and create an entire functional app from scratch. By the end, you’ll have tried out many areas of iPhone development, and ready to dig in further.
So what’s the app we’re going to make? Well, there’s a story behind that…
The other night, I saw a picture of a Potato Bug for the first time and started freaking out because it was so big and ugly! Then I got obsessed with looking up all kinds of scary bug pictures online. So to spread the fun, we’re going to make an app for that – rating scary bugs!
While making this app, we’ll cover some of the most commonly used topics in iPhone development:

  • What You Need to Get Started with iPhone Development
  • How to store your app data in a Model
  • How to use Table Views – including adding and deleting rows
  • How to create a detail view for a row
  • How to support both Portrait & Landscape orientations
  • How to use Navigation Controllers
  • How to use an Image Picker
  • How to use common controls such as a text field, button, and image view
  • How to add icons and default images
  • Bonus: How to handle long-running operations
It sounds like a lot, but don’t get scared – we’re not afraid of no bugs!
In this first part of this three-part series, we’ll cover how to load our model with a list of bugs and display them in a table view. (Jump to Part 2 or Part Three)
This tutorial is for beginner iOS developers, however it assumes you are familiar with Objective-C and programming in general. If you are new to Objective-C, I recommend reading Apple’s Objective-C Programming Language Guide first.

What You Need

First things first – to develop for the iPhone, you’ll need a Mac. Pretty much any Mac will do, as long as it’s powerful enough to run the latest version of the Mac OS, Lion. But if you’re looking to go the cheap route, you can pick up a Mac Mini for relatively cheap, and it works just fine for a development machine.
Next, you’ll need to get a copy of XCode, Apple’s IDE for iOS development. So if you haven’t already, register for a free account at the iPhone Dev Center and download a copy of Xcode from the Mac App Store.
If you’d like, you can sign up for the paid iPhone Developer Program that allows you to distribute your apps on the App Store, but if you just want to try out iOS development the free account works fine.
If you get serious about iOS development, you’ll probably want physical device(s) (iPhone/iPhone 4/iPod Touch/iPad) as well. It’s true that you can do a lot of testing with just the Simulator, but there are some APIs that don’t work on the Simulator, and you’ll need a physical device for performance testing.
That’s it – so if you haven’t already, grab a copy of XCode, fire it up, and let’s continue on!

Hello, Table View!

We’re going to start out by using one of the most common controls on the iPhone – the Table View. You’ve probably seen the Table View in a lot of apps already, here are a few examples:
UITableView Examples
So anyway, our first screen in the app will have one of these, to display a list of scary bugs!
Let’s get started by going to File\New Project in XCode, select the iOS\Application\Master-Detail Application, and click Next.
Creating an app with the master detail application template
On the next page, enter ScaryBugs for the Product Name, enter a unique string for your company identifier (com.yourcompanyname or com.yourname is best), select iPhone for the device family, and make sure Use Storyboard and Use Automatic Reference Counting is selected. Click Next when you’re done.
Project Settings for scary bugs app
Choose a place to save your project and click Create. And before we do anything else, let’s check out what we’ve got so far! In the toolbar at the top of the screen pick iPhone Simulator from the list, then click the Run button. If all goes well, you should see the following in your simulator:
The main screen created by the master detail template
You can tap the “+” button to create a new entry, and then tap the new row to see a detail view for it:
Detail view created by master detail application template
So as you can see, we already have a working project to start from since we chose the Master-Detail Application template.
We’re not going to dig into the template since that’s beyond the scope of this tutorial, but just notice that we have an empty table view and detail view set up for us and ready to go – we just have to fill it in with data!
So to do that, let’s create a class to keep track of our scary bugs.

A Scary Data Model: Organization

Notice how there’s a hierarchy of folders in the Project Navigator section of XCode:
The project navigator in Xcode
The template comes set up with a root group, and a Supporting Files group. These groups are just for organizational purposes, so feel free to change them however you want. In our case, we’re going to have a fair number of files in this project, so let’s organize things a bit.
First, create a new group to store the User Interface files in. To do this, control-click the Scary Bugs group and select New Group. Then control click the new group it created and select rename, and name it “GUI”. Drag the existing files from the root into the GUI group (but not Supporting Files). It should now look like this:
Organizing Project Navigator tree, part 1
Now create a second new group, and name it “Model”, because we’re about to add couple classes for our data model there. Your tree should now look like the following:
Creating a group for the model
Before we begin, let’s talk about how we’re going to organize things:
  1. ScaryBugData: Contains bug name and rating.
  2. ScaryBugDoc: Contains full size image, thumbnail image, ScaryBugData.
The reason we’re setting things up like that is it will make things easier in the follow-up for this tutorial, where we’re going to start saving our data to the disk, implementing file sharing, and the like.

A Scary Data Model: Implementation

Ok so let’s do it! Control-click on the Model group and click “New File…”. Select the iOS\Cocoa Touch\Objective-C class template, and click Next.
Creating a file with the Objective-C class template
Name the class ScaryBugData, enter NSObject for subclass, and click Next.
Creating a class that derives from NSObject in Xcode
In the final popup, click Create again. If all went well, your Project Navigator should now look similar to this:
Project organization, part 3
Ok, time to create our ScaryBugData class. Replace ScaryBugData.h with the following:
#import <Foundation/Foundation.h>
 
@interface ScaryBugData : NSObject
 
@property (strong) NSString *title;
@property (assign) float rating;
 
- (id)initWithTitle:(NSString*)title rating:(float)rating;
 
@end
This is pretty simple stuff – we’re just declaring an object with two properties – a string for the name of the bug, and a float for how scary we rated it. We use two property attributes for these:
  • strong: This specifies that the runtime should automatically keep a strong reference to the object. This is a fancy way of saying that the ARC runtime will keep the object in memory as long as there’s a reference to it around, and deallocate it when no references remain. For more information, check out our Beginning ARC in iOS 5 Tutorial.
  • assign: This means the property is set directly, with no memory management involved. This is what you usually set for primitive (non-object) types like a float.
We also define an initializer for the class, so we can set the title and rating when we create the bug.
Switch over to ScaryBugData.m and replace it with the following:
#import "ScaryBugData.h"
 
@implementation ScaryBugData
 
@synthesize title = _title;
@synthesize rating = _rating;
 
- (id)initWithTitle:(NSString*)title rating:(float)rating {
if ((self = [super init])) {
self.title = title;
self.rating = rating;
}
return self;
}
 
@end
Again, extremely simple stuff here. We synthesize our properties, and create our initializer to fill in our instance variables from the passed-in parameters. Note there is no need for dealloc, since we are using ARC.
Ok that’s it for ScaryBugData. Now follow the same steps you did above to create another subclass of NSObject, this time named ScaryBugDoc.
Replace ScaryBugDoc.h with the following:
#import <Foundation/Foundation.h>
 
@class ScaryBugData;
 
@interface ScaryBugDoc : NSObject
 
@property (strong) ScaryBugData *data;
@property (strong) UIImage *thumbImage;
@property (strong) UIImage *fullImage;
 
- (id)initWithTitle:(NSString*)title rating:(float)rating thumbImage:(UIImage *)thumbImage fullImage:(UIImage *)fullImage;
 
@end
Nothing of particular note here – just creating some instance variables/properties and an initializer.
Replace ScaryBugDoc.m with the following:
#import "ScaryBugDoc.h"
#import "ScaryBugData.h"
 
@implementation ScaryBugDoc
@synthesize data = _data;
@synthesize thumbImage = _thumbImage;
@synthesize fullImage = _fullImage;
 
- (id)initWithTitle:(NSString*)title rating:(float)rating thumbImage:(UIImage *)thumbImage fullImage:(UIImage *)fullImage {
if ((self = [super init])) {
self.data = [[ScaryBugData alloc] initWithTitle:title rating:rating];
self.thumbImage = thumbImage;
self.fullImage = fullImage;
}
return self;
}
 
@end
And that’s it – our data model is complete! Time to create some sample data and display it in the table view.

A Different Kind of Bug List

First, let’s set up our table view so it can handle displaying a list of ScaryBugDocs. The first thing we have to do is modify our table view so that it returns a dynamic list of rows (rather than a hardcoded single row that the template set up for us).
To do this, open MainStoryboard.storyboard. This allows you to visually layout the different “screens” in your app. As you can see, the app is currently set up to have a navigation controller (the thing that makes it easy to slide between different screens), with the root controller the “master” screen, and a secondary controller as the “detail” screen.
Main Storyboard layout
Select the Master View Controller, and in the selection area in the left panel, select the table view. In the inspector to the right, make sure the Content is set to Dynamic Prototypes.
Setting table view to use dynamic prototype cells
This is what allows you to design a single table view cell the way you like in the Storyboard editor, and easily create instances of the cell via code. We just want a basic cell, so let’s make sure the cell is using the Basic style.
Select the table view cell on the left, and in the Attributes Inspector make sure the Style is set to Basic. Also set the Identifier to MyBasicCell.
Setting cell style and reuse identifier
For more information on creating custom cells, check out our Beginning Storyboards in iOS 5 Tutorial.
OK, now that we have our table view set up correctly visually, we just need to update the code to fill in the table with a list of scary bugs.
We’ll store our ScaryBugDocs in a NSMutableArray, the collection class that you use for arrays that should be able to dynamically change in size.
Add the following line to MasterViewController.h, between the @interface and @end lines:
@property (strong) NSMutableArray *bugs;
This will be the instance variable/property that we’ll use to keep track of our list of bugs.
Now go over to MasterViewController.m and make the following changes:
// At top of file
#import "ScaryBugDoc.h"
#import "ScaryBugData.h"
 
// After @implementation
@synthesize bugs = _bugs;
 
// At the end of viewDidLoad
self.title = @"Scary Bugs";
 
// Replace the return statement in shouldAutorotateToInterfaceOrientation with:
return YES;
 
// Replace the return statement in tableView:numberOfRowsInSection with the following:
return _bugs.count;
 
// Replace tableView:cellForRowAtIndexPath with the following
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:@"MyBasicCell"];
ScaryBugDoc *bug = [self.bugs objectAtIndex:indexPath.row];
cell.textLabel.text = bug.data.title;
cell.imageView.image = bug.thumbImage;
return cell;
}
Ok, finally something interesting to discuss!
First, note that we set a property on ourselves called “title” to the string “Scary Bugs.” “title” is a special built-in property on view controllers. When a Navigation Controller displays a view controller, it shows whatever is in the “title” property in the title bar. So by setting this, we should see “Scary Bugs” up top!
Next, note we return yes in shouldAutorotateToInterfaceOrientation, which tells the OS that we should be able to support all orientations – Portrait, Landscape, and the updside down versions of each. Since this class is a UITableViewController, that’s all we have to do – the view will rotate automatically from there!
Next, when constructing a table view with dynamic rows we have to override numberOfSectionsInTableView and numberOfRowsInSection to tell the OS how many sections/rows should be displayed in the table view. We just have 1 section, so we don’t have to do anything because the template is already set up to return 1 section. For the rows, we just return the number of objects in our bugs array.
Finally, we implement tableView:cellForRowAtIndexPath, which is probably the most important method to implement when making a table view. Here, you set up the cell that will be displayed for a particular row. The OS will call this method once per row for each row so you can set it up.
Let’s take a look this method in detail, since this is particularly important:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:@"MyBasicCell"];
ScaryBugDoc *bug = [self.bugs objectAtIndex:indexPath.row];
cell.textLabel.text = bug.data.title;
cell.imageView.image = bug.thumbImage;
return cell;
}
The first line calls a helper function called “dequeueReusableCellWithIdentifier” to try to return a reusable cell. What is this all about?
Well, it’s an important performance optimization. Keep in mind that table views can contain a very large number of rows, but only a certain number of them are displayed on screen at a time. So rather than creating a new cell each time a new row cycles into the screen, the OS can improve performance by re-using a cell that was already created, but scrolled off-screen.
So that’s what the dequeueReusableCellWithIdentifier call is. If there’s not a reusable cell available, we just create a new cell based on the cell you set up in Interface Builder (remember how we set it as basic, and named it “MyBasicCell”?).
In the Storyboard editor you can customize the layout of the cell, or use one of the built-in ones. In our case, we chose the basic style, which adds a label and image you can set.
If you’re curious what the different standard table view cell options look like, check out the “Standard Styles for Table-View Cells” section in the Table View Programming Guide.
Finally, we configure the cell by setting its textLabel and imageView (which are available with the Basic style).
Believe it or not that’s all we need to do! Now we just need to set up some sample data for the table view to display.

Scary Bug Pictures!

But of course we’ll need some scary bug pictures for that! You can either browse the Internet and find some, or download these Scary Bug Pictures I found on stock.xchng.
Once you’ve downloaded the files or gotten your own, drag them all into the root of your Project Navigator tree. When the popup appears, make sure “Copy items into destination group’s folder (if needed)” is checked, and click Add.
Adding scary bugs files to project
Then open up AppDelegate.m and make the following changes:
// At top of file
#import "MasterViewController.h"
#import "ScaryBugDoc.h"
 
// At beginning of application:didFinishLaunchingWithOptions
ScaryBugDoc *bug1 = [[ScaryBugDoc alloc] initWithTitle:@"Potato Bug" rating:4 thumbImage:[UIImage imageNamed:@"potatoBugThumb.jpg"] fullImage:[UIImage imageNamed:@"potatoBug.jpg"]];
ScaryBugDoc *bug2 = [[ScaryBugDoc alloc] initWithTitle:@"House Centipede" rating:3 thumbImage:[UIImage imageNamed:@"centipedeThumb.jpg"] fullImage:[UIImage imageNamed:@"centipede.jpg"]];
ScaryBugDoc *bug3 = [[ScaryBugDoc alloc] initWithTitle:@"Wolf Spider" rating:5 thumbImage:[UIImage imageNamed:@"wolfSpiderThumb.jpg"] fullImage:[UIImage imageNamed:@"wolfSpider.jpg"]];
ScaryBugDoc *bug4 = [[ScaryBugDoc alloc] initWithTitle:@"Lady Bug" rating:1 thumbImage:[UIImage imageNamed:@"ladybugThumb.jpg"] fullImage:[UIImage imageNamed:@"ladybug.jpg"]];
NSMutableArray *bugs = [NSMutableArray arrayWithObjects:bug1, bug2, bug3, bug4, nil];
 
UINavigationController * navController = (UINavigationController *) self.window.rootViewController;
MasterViewController * masterController = [navController.viewControllers objectAtIndex:0];
masterController.bugs = bugs;
Here we just use the ScaryBugDoc initializer to create four sample bugs, passing in the title, rating, and images for each. We add them all to a NSMutableArray, and set them on our table view.
Speaking of which, we can get a pointer to the RootViewController since we know it’s the first view controller in the navigation controller’s stack. There are other ways we could have gotten a pointer as well, but this is one easy way.
And that’s it! Compile and run your app, and if all works well, you should see a list of (mostly) frightening bugs in your table view!
Scary Bugs Table View

Where To Go From Here?

Here is a sample project with all of the code we’ve developed so far in this tutorial series.
Please let me know if anything in the above is confusing or if you’d like me to go into more detail about anything.
Next in the series, we cover how to create a detail view for the bugs so we can edit and rate our bugs!

No comments:

Post a Comment