25 August 2010

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. (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, 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!


Category: iPhone

Tags: , , ,

37 Comments

  1. Dad (44 comments) says:

    Nice! Might want to temper that “any mac” comment to be “any Intel based Mac that can run 10.6.4″….

  2. Jim Murff (17 comments) says:

    Wow Ray! Great job. You are going to put us all out of business explaining the black arts this way :) LOL
    -jim

  3. Raj (3 comments) says:

    Ray,
    Its wonderful tutorial and very brief coverage or all topics. I really enjoyed going through the article. I’m waiting for the future tutorial article.

    Thanks,
    Raj

  4. Thang Tran (21 comments) says:

    Great post Ray! Simple and informative… let’s ride on ;)

  5. macbirdie (2 comments) says:

    Hey, I’m pretty new in Objective-C, but I think that if the title property in ScaryBugData has a copy attribute, you don’t have to copy the data assigned to it in the initWithTitle message. Otherwise you’re essentially copying the data twice.

  6. jason (19 comments) says:

    quick question about your variable naming conventions. In your .h file you have:

    UIImage *_thumbImage;
    @property (retain) UIImage *thumbImage;

    Don’t they have to be the same name? What’s the point of the _?

    Then you have in your .m similar stuff:

    @synthesize thumbImage = _thumbImage;

    _thumbImage = [thumbImage retain];

    I was getting confused reading this. Can you clarify?

  7. jm (1 comments) says:

    Great tuto !
    I had to add
    #import “RootViewController.h”
    to ScaryBugsAppDelegate.m to compile
    The line is in the sample project but not in this current page…
    Thx a lot

  8. Ray Wenderlich (874 comments) says:

    @Dad: Thanks, I’ve updated the article to mention that!

    @Jim: Ha ha… thanks! :]

    @Raj, @Thang: Glad you guys like it, second part is coming soon!

    @macbirdie, @jason: Yeah, I get a ton of questions about properties and my conventions with handling them, I’m going to write a post about that specifically at some point.

    But until then, let me answer your questions :]

    There is a difference between the class’s instance variable (_title) and the class’s property (title). By setting the copy attribute on the property (title) and synthesizing the property, the compiler automatically creates “setTitle” and “title” methods on the class for us. It is true that we can ues the “setTitle” attribute to pass in a string, and the generated method will automatically call copy on the passed in string for us.

    You can call setTitle either by [self setTitle:@"my string"] or self.title = @”my string” – those are equivalent. However, we’re not using the property (or setTitle) in the initializer – we’re accessing the class’s instance variable _title directly, hence we need to manually call copy ourselves.

    Abd that leads into @jason’s question, the reason I name the instance variables with an underscore and the properties wihout an underscore is so it’s very easy to understand which you’re using at a glance while looking at the code. Some people like to name their properties with the same name as their instance variables, but I personally find it easier to read/understand when the names are different.

    Let me know if you have any further questions on this!

    @jm: Whoops – thanks for catching that, I’ve updated the tutorial to include that!

  9. macbirdie (2 comments) says:

    Thanks for the answer. I forgot that giving “= name” after property’s @synthesize makes the “name” represent the instance variable. All’s there in Apple’s docs!

  10. Marin Todorov (19 comments) says:

    Hi Ray,
    remember some time ago I was mentioning that your great tutorials would make a great online book?
    Have a look here – this plugin can turn a WP3 into different electronic book formats – looks tailored to the idea : http://anthologize.org/

    Marin

  11. Ray Wenderlich (874 comments) says:

    @Marin: That looks pretty cool, thanks for letting me know about that!

  12. Kelvin Kao (1 comments) says:

    I like how you subtly introduced the separation of view controllers and models right off the bat!

  13. Nirbhay (6 comments) says:

    Very well written tutorial. Great job Ray!

  14. Ray Wenderlich (874 comments) says:

    @Kelvin: Yeah separating the view controller and model keeps the code a LOT cleaner and easier to work with, so wanted to get that in early :]

  15. Jason (19 comments) says:

    Im having a problem implementing this in a tab bar, is there another way to point to the tables data here is where Im having the problem. Thank you
    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;

  16. Ray Wenderlich (874 comments) says:

    @Jason: What is the specific problem you’re having? Is it that you don’t know how to access the RootViewController given that you are placing it inside a tab bar? Have you set up a tab bar view controller with the RootViewController as one of the tabs?

    If so, you can create a RootViewController variable inside your class, and make a property for it marked as an IBOutlet. Then you can control-drag from the RootViewController in your XIB to the outlet, to connect it. Then inside the code, you will now have access to the RootViewController to access programmatically so you can set the bugs property to the list of bugs.

  17. lee (6 comments) says:

    Hi Ray,

    Ive followed your tutorial step by step, everything compiles ok but i have a blank screen still after ive inserted images.

    Any ideas?

  18. G (1 comments) says:

    Hello,
    I am just starting this tutorial (thnx for making this great stuff)

    and what I don’t understand is why you use the underscore in the interface, but leave it out when you make it a property.????

    I’ve learned in another book this should always be the same names….

    maybe you can answer me.
    thnx
    G

  19. Nash (2 comments) says:

    This might sound stupid but can any1 explain the following lines of code in layman’s terms

    RootViewController *rootController = (RootViewController *) [navigationController.viewControllers objectAtIndex:0];

    rootController.bugs = bugs;

    thanks

  20. Dad (44 comments) says:

    @Nash sure. start with the right part:

    navigationController.viewControllers

    is getting the viewControllers NSArray instance variable from the navigationController object (our instance variable) using “dot notation” (see the Objective C 2.0 language guide at developer.apple.com).

    Then we are calling the ObjectAtIndex: method of NSArray to get the first item out of the array (first item is at 0 index):

    [navigationController.viewControllers objectAtIndex: 0]

    The next part:

    (RootViewController *)

    is a cast to tell the compiler that the object returned by objectAtIndex: is a RootViewController (if you look at the definition of ObjectAtIndex: in NSArray.h you’ll see that it is defined to return a generic objectiveC object: “id”).

    Then we assign the cast result of ObjectAtIndex: to a temporary local variable which is declared to be a RootViewController *

    RootViewController *rootController =

    And finally, we set the value of the rootController object’s “bugs” instance variable to the local variable “bugs”.

    rootController.bugs = bugs;

    If one wanted to make this code more bulletproof, one could test that the object pulled out of the viewControllers array was actually a RootViewController* by changing the code to have:

    if ( [rootController isKindOfClass: [RootViewController class]] )
    {
    rootController.bugs = bugs;
    }

    that way we wouldn’t try to access the “bugs” instance variable on an object that didn’t have one.

    hope that helps!

  21. Ray Wenderlich (874 comments) says:

    @Iee: What do you mean by blank screen? Are you at least seeing the table view, but no entries inside?

    If that’s the case, make sure that you populate the array of bugs in the app delegate and set it on the rootviewcontroller. Then set a breakpoint inside tableView:cellForRowAtIndexPath to make sure that it’s trying to access your cell data.

    If all else fails, try comparing your code to the sample project to see what might be different.

    @G: Check my response to that a few questions up in the comments here.

    @Dad: Thanks for the great & detailed explanation and helping out Nash!

  22. Nash (2 comments) says:

    @Dad
    thanks a lot :)

    @Ray
    Thanks for the tutorial :)

  23. neuron (1 comments) says:

    Hi!

    Firstly, thanks for this great tutorial. I’ve been going through the Stanford University tutorials but decided I need to make something too.

    I have a problem though. I’ve just updated the SDK to 4.1. My code all compiles fine and looks good. Simulates and show the table view but will not tap through to the full size image.

    I’ve just tried your sample code which also does the same after adjusting the project to use the 4.1 simulator.

    Am I right in thinking that I cannot actually test this on my 3GS unless I pay the $99?

    Is this just an issue with the simulator?

  24. rafiq (4 comments) says:

    how to add names r values to table view when we have created table view through code
    created table view as
    uitalleview * tab==[[uitableview alloc]initwithframe:cgrectmake90.0,0.0,100,120];
    [self.view addsubview:tab];

  25. Mahmud Ahsan (6 comments) says:

    Nice article Ray, very helpful and easy to understand each part. Carry on bro.

  26. M.M.H.Masud (1 comments) says:

    Nice one. simple and easy to understand. very useful for the new bees.

    keep going…

  27. Didder (1 comments) says:

    I am new to this so please go easy on me lol. Where is the scarybugdoc.h file thanks

  28. Darren (1 comments) says:

    Great tutorials.
    Im just starting out with this new iOS dev hobby and your tuts are by far the best Ive seen online yet. I absolutely love how you cover just about everything needed within this one app.
    Im trying to use what ive learned here in my own app but have come up with a few snags im trying to figure out. but it sure is fun!!!!!…with your help :)

  29. Ray Wenderlich (874 comments) says:

    @neuron: Cool! I’ve heard great things about the Stanford tuts.

    Regarding your question, this part of the tutorial doesn’t go to the full size image yet – that’s in part 2! :] Keep going to the next one and you should be good.

    @Didder: You need to create ScaryBugDoc.m and ScaryBugDoc.h/m, following the same instructions you used to create ScaryBugData.h/m.

    @rafiq: That’s not the correct way – you should call initWithTableViewStyle:UITableViewCellStyleDefault. Then you can set the text with the textLabel property, the image with the image property, etc.

    @Darren, @Masud, @Mahmud: Thanks so much!

  30. Kendall Jones (1 comments) says:

    Thanks for posting this tutorial. It’s VERY well written and extremely helpful!!

  31. Ray Wenderlich (874 comments) says:

    @Kendall: Thanks so much for the kind words!

  32. Andrew (8 comments) says:

    Great tutorial. Very easy to follow example, and very well explained.

    I didn’t see the code snippet of the method numberOfSectionsInTableView for RootViewController (but that may default anyway).

    Again – great tutorial.

  33. Dil (1 comments) says:

    Thank you for the great tutorial to get me started, but I’m having one issue.
    When the simulator comes up I see the table view just fine, but then when I try to click on the table cells it doesn’t take me to the full picture. Please help !

  34. hiessu (1 comments) says:

    I’m new to Iphone development.I’ve watched some stanford tutorials(2010 autumn).I’ve noticed some differences and have questions.
    For example , In ScaryBugData.m
    self.thumbImage = thumbImage; instead of _thumbImage = [thumbImage retain];
    when dealloc
    self.thumbImage = nil; instead of [_thumbImage release]; _thumbImage = nil;

    is there a better way?

    Thank you for posting great tutorials!

I'd love to hear your thoughts!