iOS Tutorial: How To Create A Simple iPhone App: Part 1/3
An iOS tutorial for complete beginners that shows you how to make your first iPhone app, from scratch! By Ray Wenderlich.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
iOS Tutorial: How To Create A Simple iPhone App: Part 1/3
20 mins
Update 6/02/14: Fully updated for iOS 7 by Jorge Jordán.
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 an iOS tutorial series tailored for beginners.
But rather than focusing in detail on just one topic, you’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 you’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, you’re going to make an app for that – rating scary bugs!
While making this app, you’ll learn 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 – you’re not afraid of no bugs!
In this first part of this three-part iOS tutorial series, you’ll learn how to load your model with a list of bugs and display them in a table view. (Jump to Part Two or Part Three)
This iOS 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, Mavericks. 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 4/iPhone 5/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 continue on!
Hello, Table View!
You’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:
So anyway, your first screen in the app will have one of these, to display a list of scary bugs!
Start by going to File\New Project… in XCode, select the iOS\Application\Master-Detail Application, and click Next.
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 on the Devices drop down and enter RWT as Class Prefix. Click Next when you’re done.
Choose a place to save your project and click Create. And before you do anything else, check out what you’ve got so far! In the main menu choose Product\Destination\iOS Simulator\iPhone Retina (3.5-inch), then click the Run button. If all goes well, you should see the following in your simulator:
You can tap the + button to create a new entry, and then tap the new row to see a detail view for it:
So as you can see, you already have a working project to start from since you chose the Master-Detail Application template.
You’re not going to dig into the template since that’s beyond the scope of this iOS tutorial, but just notice that you have an empty table view and detail view set up and ready to go – you just have to fill it in with data!
So to do that, create a class to keep track of your scary bugs.
A Scary Data Model: Organization
Notice how there’s a hierarchy of folders in the Project Navigator section of 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 your case, you’re going to have a fair number of files in this project, so it’s better to 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:
Now create a second new group, and name it Model, because you’re about to add couple classes for your data model there. Your tree should now look like the following:
Before you begin, it’s important for you to know how things are going to be organized on this tutorial:
- RWTScaryBugData: Contains bug name and rating.
- RWTScaryBugDoc: Contains full size image, thumbnail image, RWTScaryBugData.
The reason you’re setting things up like that is it will make things easier in the follow-up for this iOS tutorial, where you’re going to start saving your data to the disk, implementing file sharing, and the like.
A Scary Data Model: Implementation
Ok so Control-click on the Model group and click New File…. Select the iOS\Cocoa Touch\Objective-C class template, and click Next.
Name the class RWTScaryBugData, enter NSObject for subclass, and click Next.
In the final popup, click Create again. If all went well, your Project Navigator should now look similar to this:
Ok, time to create your RWTScaryBugData class. Replace RWTScaryBugData.h with the following:
#import <Foundation/Foundation.h>
@interface RWTScaryBugData : NSObject
@property (strong) NSString *title;
@property (assign) float rating;
- (id)initWithTitle:(NSString*)title rating:(float)rating;
@end
This is pretty simple stuff – you’re just declaring an object with two properties – a string for the name of the bug, and a float for how scary you rated it. You 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 your 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.
You also define an initializer for the class, so you can set the title and rating when you create the bug.
Switch over to RWTScaryBugData.m and replace it with the following:
#import "RWTScaryBugData.h"
@implementation RWTScaryBugData
@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. You synthesize your properties, and create your initializer to fill in your instance variables from the passed-in parameters. Note there is no need for dealloc, since you are using ARC.
Ok that’s it for RWTScaryBugData. Now follow the same steps you did above to create another subclass of NSObject, this time named RWTScaryBugDoc.
Replace RWTScaryBugDoc.h with the following:
#import <Foundation/Foundation.h>
@class RWTScaryBugData;
@interface RWTScaryBugDoc : NSObject
@property (strong) RWTScaryBugData *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 RWTScaryBugDoc.m with the following:
#import "RWTScaryBugDoc.h"
#import "RWTScaryBugData.h"
@implementation RWTScaryBugDoc
@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 = [[RWTScaryBugData alloc] initWithTitle:title rating:rating];
self.thumbImage = thumbImage;
self.fullImage = fullImage;
}
return self;
}
@end
And that’s it – your data model is complete! Time to create some sample data and display it in the table view.
A Different Kind of Bug List
First, you’ll set up your table view so it can handle displaying a list of RWTScaryBugDocs. The first thing you have to do is modify your table view so that it returns a dynamic list of rows (rather than a hardcoded single row that the template set up for you).
To do this, open Main.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.
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.
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. You just want a basic cell, so 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.
For more information on creating custom cells, check out your Beginning Storyboards in iOS 5 Tutorial.
OK, now that you have your table view set up correctly visually, you just need to update the code to fill in the table with a list of scary bugs.
You’ll store your RWTScaryBugDocs 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 RWTMasterViewController.h, between the @interface and @end lines:
@property (strong) NSMutableArray *bugs;
This will be the instance variable/property that you’ll use to keep track of your list of bugs.
Now go over to RWTMasterViewController.m and make the following changes:
// At top of file
#import "RWTScaryBugDoc.h"
#import "RWTScaryBugData.h"
// After @implementation
@synthesize bugs = _bugs;
// At the end of viewDidLoad
self.title = @"Scary Bugs";
// 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"];
RWTScaryBugDoc *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 you set a property 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, you should see “Scary Bugs” up top!
Next, when constructing a table view with dynamic rows you have to override numberOfSectionsInTableView and numberOfRowsInSection to tell the OS how many sections/rows should be displayed in the table view. You just have 1 section, so you don’t have to do anything because the template is already set up to return 1 section. For the rows, you just return the number of objects in your bugs array.
Finally, you 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.
Take a look to this method in detail, since this is particularly important:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:@"MyBasicCell"];
RWTScaryBugDoc *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, you just create a new cell based on the cell you set up in Interface Builder (remember how you 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 your case, you 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, you configure the cell by setting its textLabel and imageView (which are available with the Basic style).
Believe it or not that’s all you need to do! Now you just need to set up some sample data for the table view to display.
Scary Bug Pictures!
But of course you’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 Finish.
Then open up RWTAppDelegate.m and make the following changes:
// At top of file
#import "RWTMasterViewController.h"
#import "RWTScaryBugDoc.h"
// At beginning of application:didFinishLaunchingWithOptions
RWTScaryBugDoc *bug1 = [[RWTScaryBugDoc alloc] initWithTitle:@"Potato Bug" rating:4 thumbImage:[UIImage imageNamed:@"potatoBugThumb.jpg"] fullImage:[UIImage imageNamed:@"potatoBug.jpg"]];
RWTScaryBugDoc *bug2 = [[RWTScaryBugDoc alloc] initWithTitle:@"House Centipede" rating:3 thumbImage:[UIImage imageNamed:@"centipedeThumb.jpg"] fullImage:[UIImage imageNamed:@"centipede.jpg"]];
RWTScaryBugDoc *bug3 = [[RWTScaryBugDoc alloc] initWithTitle:@"Wolf Spider" rating:5 thumbImage:[UIImage imageNamed:@"wolfSpiderThumb.jpg"] fullImage:[UIImage imageNamed:@"wolfSpider.jpg"]];
RWTScaryBugDoc *bug4 = [[RWTScaryBugDoc 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;
RWTMasterViewController *masterController = [navController.viewControllers objectAtIndex:0];
masterController.bugs = bugs;
Here you just use the RWTScaryBugDoc initializer to create four sample bugs, passing in the title, rating, and images for each. You add them all to a NSMutableArray, and set them on your table view.
Speaking of which, you can get a pointer to the RootViewController since you know it’s the first view controller in the navigation controller’s stack. There are other ways you 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!
Where To Go From Here?
Here is a sample project with all of the code you’ve developed so far in this iOS 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 iOS tutorial series, you’ll learn how to create a detail view for the bugs so you can edit and rate your bugs!
All videos. All books.
One low price.
A Kodeco subscription is the best way to learn and master mobile development — plans start at just $19.99/month! Learn iOS, Swift, Android, Kotlin, Flutter and Dart development and unlock our massive catalog of 50+ books and 4,000+ videos.
Learn more