Thursday, 13 October 2011

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



iPhone programming is like a ladybug - fun and only a little scary!
iPhone programming is like a ladybug - fun and only a little scary!
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.
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, Snow Leopard. 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.
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, choose Application under iPhone OS, Navigation-based Application from the list on the right, and click “Choose…”:
Navigation Based App
Name the project ScaryBugs and click Save. 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 Simulator from the list, then go to Build\Build and Run. If all goes well, you should see the following in your simulator:
Empty Table View
So as you can see, we already have a working project to start from since we chose the Navigation-based 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 all 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 Groups & Files section of XCode:
Groups And Files for our Project
The template comes set up with several groups – Classes, Other Sources, Resources, etc. 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, rename the existing group named “Classes” to “View Controllers”. You can do this by right clicking on the group, clicking “Rename”, and naming it “View Controllers.”
Then right click on the “ScaryBugs” project under Groups & Files, click “Add\New Group”. Name the Group “Model”, because we’re about to add couple classes for our data model there.
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! Right click on the Model group and click “Add\New File…”. Under iPhone OS, choose “Cocoa Touch Class”, then “Objective-C class”, make sure “Subclass of NSObject” is selected, and click Next:
Add New File
Name the file ScaryBugData.m, make sure “Also create ScaryBugData.h” is checked, and click Finish. If all went well, your Groups & Files should now look similar to this:
Organized Groups and Files
Ok, time to create our ScaryBugData class. Replare ScaryBugData.h with the following:
#import <Foundation/Foundation.h>
 
@interface ScaryBugData : NSObject {
    NSString *_title;
    float _rating;
}
 
@property (copy) NSString *title;
@property  float rating;
 
- (id)initWithTitle:(NSString*)title rating:(float)rating;
 
@end
This is pretty simple stuff – we’re just declaring an object with two instance variables – a string for the name of the bug, and a float for how scary we rated it.
We also declare a property for each of the instance variables, and define an initializer for the class.
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])) {
        _title = [title copy];
        _rating = rating;
    }
    return self;
}
 
- (void)dealloc {
    [_title release];
    _title = nil;    
    [super dealloc];
}
 
@end
Again, extremely simple stuff here. We synthesize our properties, create our initializer to fill in our instance variables from the passed-in parameters, and release our variables in dealloc.
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 {
    ScaryBugData *_data;
    UIImage *_thumbImage;
    UIImage *_fullImage;
}
 
@property (retain) ScaryBugData *data;
@property (retain) UIImage *thumbImage;
@property (retain) 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])) {
        _data = [[ScaryBugData alloc] initWithTitle:title rating:rating];
        _thumbImage = [thumbImage retain];
        _fullImage = [fullImage retain];
    }
    return self;
}
 
- (void)dealloc {
    [_data release];
    _data = nil;   
    [_fullImage release];
    _fullImage = nil;
    [_thumbImage release];
    _thumbImage = nil;
    [super dealloc];
}
 
@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. 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.
Make the following changes to RootViewController.h:
// Inside @interface
NSMutableArray *_bugs;
 
// After @interface
@property (retain) 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 RootViewController.m and make the following changes:
// At top of file
#import "ScaryBugDoc.h"
#import "ScaryBugData.h"
 
// After @implementation
@synthesize bugs = _bugs;
 
// Uncomment viewDidLoad and add the following inside
self.title = @"Scary Bugs";
 
// Uncomment shouldAutorotateToInterfaceOrientation, and modify the return statement to:
return YES;
 
// Replace the return statement in tableView:numberOfRowsInSection with the following:
return _bugs.count;
 
// Inside tableView:cellForRowAtIndexPath, add the following after "Configure the cell" but before return cell:
ScaryBugDoc *doc = [_bugs objectAtIndex:indexPath.row];
cell.textLabel.text = doc.data.title;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.imageView.image = doc.thumbImage;
 
// Add inside dealloc
[_bugs release];
_bugs = nil;
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 you always 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 at the entire method, since this is particularly important:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
 
    static NSString *CellIdentifier = @"Cell";
 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
    }
 
    // Configure the cell.
    ScaryBugDoc *doc = [_bugs objectAtIndex:indexPath.row];
    cell.textLabel.text = doc.data.title;
    cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
    cell.imageView.image = doc.thumbImage;
 
    return cell;
}
The first two lines call 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. You can create your own table view cells, or use a standard one. In our case, the default table view works fine, so we just pick that by setting the style to UITableViewCellStyleDefault.
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 default 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 Resources group of your XCode project. When the popup appears, make sure “Copy items into destination group’s folder (if needed)” is checked, and click Add.
Add Resources
Then open up ScaryBugsAppDelegate.m and make the following changes:
// At top of file
#import "ScaryBugDoc.h"
#import "RootViewController.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"]] autorelease];
ScaryBugDoc *bug2 = [[[ScaryBugDoc alloc] initWithTitle:@"House Centipede" rating:3 thumbImage:[UIImage imageNamed:@"centipedeThumb.jpg"] fullImage:[UIImage imageNamed:@"centipede.jpg"]] autorelease];
ScaryBugDoc *bug3 = [[[ScaryBugDoc alloc] initWithTitle:@"Wolf Spider" rating:5 thumbImage:[UIImage imageNamed:@"wolfSpiderThumb.jpg"] fullImage:[UIImage imageNamed:@"wolfSpider.jpg"]] autorelease];
ScaryBugDoc *bug4 = [[[ScaryBugDoc alloc] initWithTitle:@"Lady Bug" rating:1 thumbImage:[UIImage imageNamed:@"ladybugThumb.jpg"] fullImage:[UIImage imageNamed:@"ladybug.jpg"]] autorelease];
NSMutableArray *bugs = [NSMutableArray arrayWithObjects:bug1, bug2, bug3, bug4, nil];
RootViewController *rootController = (RootViewController *) [navigationController.viewControllers objectAtIndex:0];
rootController.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!

0 comments:

Post a Comment

 

Copyright @ 2013 PakTechClub.