iOS Programming Recipe 11: Using The UITableView Part II

In Part I of this recipe we left off with a UITableView that Displayed some creatures which were grouped by type. In this Recipe we’ll be extending the functionality a bit and emplement a delegate in order to handle a touch event. In addition, we’ll take a minute to explore styling of the table view and cells.

Assumptions

  • You Have already read through Part I, if not read through that first.

Styling The Table View

The next two sections will be brief, but I thought it was worth visiting since there are a few things that can be done right from the property inspector.

First of choose the tableview object from the storyboard and go to the property inspector tab on the top right hand pane. Choose Grouped from style section and go ahead and change default to some color, I change mine to blue.

Changing Table View Properties

Now run the application and you should have a style similar to this:

Changed Table View

Styling The Table View Cell

Select the Cell in the storyboard and from the property inspector change the style from “Basic” to “Subtitle.” Then Accesory from none to “Disclosure Indicator”. From the storyboard, click and drag the handle on the cell to resize it to a 80 point height.

Changing Table Cell Properties

Go ahead and run the app again and you should see the changes to the cell we made.

Change Table Cell

You’ll notice that the subtitle just says Detail for all of these cells. To fix that, we’ll need to update the the cellForRowAtIndexPath method to account for this. While we’re at it we’re gonna go ahead and add some images to the cell as well. We’ll start by adding the subtitle. The Subtitle will just be scientific name for each animal. We’ll go ahead and create two more arrays to hold these values so go to your ViewController.h file and modify it as follows:


@interface ViewController : UIViewController    <UITableViewDataSource, UITableViewDelegate>

@property (strong, nonatomic) NSArray *bugs;
@property (strong, nonatomic) NSArray *animals;
@property (strong, nonatomic) NSArray *scientificBugs;
@property (strong, nonatomic) NSArray *scientificAnimals;

@end

Now we’ll need to add the arrays viewDidLoad method of the ViewController.m file.


- (void)viewDidLoad
{
self.bugs = @[@"Spider",@"Ladybug",@"Firefly"];
self.animals = @[@"Cat",@"Dog",@"Bigfoot"];
self.scientificBugs = @[@"Araneae",@"Coccinellidae",@"Lampyridae"];
self.scientificAnimals = @[@"Felis catus",@"Canis lupus familiaris",@"Homo sapiens hirsutii"];
[super viewDidLoad];
}

Ok! Now we’re finally able to change the cellForRowAtIndexPath Method:


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"creatures"];

    switch (indexPath.section)
    {
        case bugsSection:
            cell.textLabel.text = self.bugs[indexPath.row];
            cell.detailTextLabel.text = self.scientificBugs[indexPath.row];
            break;
        case animalsSection:
            cell.textLabel.text = self.animals[indexPath.row];
            cell.detailTextLabel.text = self.scientificAnimals[indexPath.row];
            break;
        default:
            cell.textLabel.text = @"Not Found";
    }

    return cell;
}

That was pretty easy eh? Now let’s get slightly more complicated and add an image to each table cell. I snagged these images off of wikipedia and resized them and saved them as .png . It’s worth quickly discussing points vs pixels. All of UIKit is built on the notion of points (non pixels). So take our table cell for example, which has a height of 80, that is specifically 80 points not pixels. On the 3GS and previous models of the iPhone, 1 pixel is equal to 1 point, but the iPhone 4 and newer (retina display) 1 point is equal to 2 pixels. So in order for our cell image to look good on both retina and non-retina devices we need to supply both 1x and 2x graphics. So to fill our 80 point cell we’ll need both 80px x 80px and 160px x 160px versions of each image.

We’ll start by adding the images to the project. This is pretty easy, Just find the images in the finder and drag them over to the file listing in the project navigator. I added a new folder titled images and drug them into that.

Your File names should be as follows:

Spider.png
Spider@2x.png
Ladybug.png
Ladybug@2x.png
Firefly.png
Firefly@2x.png
Cat.png
Cat@2x.png
Dog.png
Dog@2x.png
Bigfoot.png
Bigfoot@2x.png

The @2x denotes the twice resolution image. No need to deliniate this in code, the SDK is smart enough to choose which one is needed depending on the resolution needed. Once copied into the folder you should have a listing similar to this :

File listing

Alright , now we need to modify the cellForRowAtIndexPath again. I changed it as follows:


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"creatures"];

    UIImage *creatureImage = nil;
    switch (indexPath.section)
    {
        case bugsSection:
            cell.textLabel.text = self.bugs[indexPath.row];
            cell.detailTextLabel.text = self.scientificBugs[indexPath.row];
            creatureImage = [UIImage imageNamed:self.bugs[indexPath.row]];
            cell.imageView.image = creatureImage;
            break;
        case animalsSection:
            cell.textLabel.text = self.animals[indexPath.row];
            cell.detailTextLabel.text = self.scientificAnimals[indexPath.row];
            creatureImage = [UIImage imageNamed:self.animals[indexPath.row]];
            cell.imageView.image = creatureImage;
            break;
        default:
            cell.textLabel.text = @"Not Found";
    }

    return cell;
}

Ok So the first thing I did to this method was created a new UIImage object with the variable name creatureImage.

Then I just set the imageView of the cell to creatureImage object.

Now go ahead and run it and you should see some images!

Cells with subtitles and Images

Alright, now we’re ready to implement the delegate. This delegate will just give a popup with some information about each creature.

Implementing The Delegate

In the class for the UITableView we’ll see that there are a number of delegate methods to choose from. The one we’ll want to use for this example is didSelectRowAtIndexPath. so start by adding that to the end of the ViewController.m file like so:


- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
}

Ok! now we’re about to add a whole bunch of stuff to it. Admittedely, this Alert is not a very helpful prompt as it just shows what is on the detail but it still gets the point across.  This next bit of code creates a new NSString object to hold our formatted string and also a new UIAlertView object to show that string in a prompt. The case statement we’re creating figures out which section the cell you selected was in. Then based on the section, we’ll choose a formatted string that uses the appropriate arrays to populate our formatted string with values based on the cell row selected. So Here it is!


- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    UIAlertView *viewCreatureInfo = nil;
    NSString   *creatureInfo = nil;

    switch (indexPath.section){
        case bugsSection:
            creatureInfo = [[NSString alloc] initWithFormat:@"Name:%@ Scientific Name: %@", [self.bugs objectAtIndex:indexPath.row],[self.scientificBugs objectAtIndex:indexPath.row] ];
                break;
        case animalsSection:
            creatureInfo = [[NSString alloc] initWithFormat:@"Name:%@ Scientific Name: %@", [self.Animals objectAtIndex:indexPath.row],[self.scientificAnimals objectAtIndex:indexPath.row] ];
                break;

    viewCreatureInfo = [[UIAlertView alloc] initWithTitle: @"Creature" message:creatureInfo delegate: nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
}

    [viewCreatureInfo show];
}

Alright, Now there is one more thing to do. Just like you did in Part 1 to connect the data source to the view controller using the connections inspector and the Document outline. Go ahead and connect the delegate to the view as well.

Now you can go ahead and build and run your application and click one of the creatures. Then you should see the prompt. Mine looked like this:

Final Product

Ok! That concludes this two part series. For future reference rather than creating a bunch of arrays for these names and descriptions, creating a data model would be much better.

Now You might be thinking: “What if I want to go to another view based on a table view selection?”. For that situation it would probably be best to use UINavigationController. I’ll be covering that subject soon.

About Joe Hoffman

I am An Electrical Engineer that spends a lot of my off time doing web development and other programming. Currently I am trying to expand my knowledge with iOS and I find that writing tutorials helps me learn more thoroughly as well as provide some useful info to others.

Comments

  1. Just an FYI… in your didSelectRowAtIndexPath you forgot to add break in the switch statements. And as a result you will always show the names for the animalsSection when you choose the first section (bugs)

    switch (indexPath.section) {
    case bugsSection:
    creatureInfo = [[NSString alloc] initWithFormat:@”Name: %@ Scientific\nName: %@”, [self.bugs objectAtIndex:indexPath.row],[self.scientificBugs objectAtIndex:indexPath.row]];
    break;
    case animalsSection:
    creatureInfo = [[NSString alloc] initWithFormat:@”Name: %@ Scientific\nName: %@”, [self.animals objectAtIndex:indexPath.row],[self.scientificAnimals objectAtIndex:indexPath.row]];
    break;
    }

  2. Ummm …

    Didn’t this just break the app? Mine wouldn’t compile until I changed the top line of

    
    
    @interface ViewController : UIViewController    <UITableViewDataSource, UITableViewDelegate

    @property (strong, nonatomic) NSArray *bugs;
    @property (strong, nonatomic) NSArray *animals;
    @property (strong, nonatomic) NSArray *scientificBugs;
    @property (strong, nonatomic) NSArray *scientificAnimals;

    @end

    back to what it was in the first part

    
    
    @interface ViewController : UIViewController

    Thanks again for such a great tutorial – I wouldn’t have been able to learn much about programming otherwise!

    • Joe Hoffman says:

      Hi Evan,

      Without declaring the table view delegate and data source protocols, your tableview won’t behave correctly. The data source is needed to populate the value inside it. and the delegate is needed to know when a table view cell was selected.

  3. Hi,

    I followed the tutorial to the letter, but in the end, Alert is not showing. I tried both, connecting delegate to he View and to the ViewController, but to no avail.

    My code looks exactly as the one in the tutorial. What could be the culprit.
    Btw, Im using Xcode 5.0.2

    • Actually, adding or removing entire didSelectRowAtIndexPath makes no difference in the appearance or functionality of the app at all.
      What am I missing?

      • Hey Dusan i had the same problem but i found a solution.

        I wrote a simple NSLog into the delegate method just to be sure that the method will be called at runtime. So the code inside the (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath method works perfectly but the method did not get call. You have to tell your ViewController (the delegate) that it have to delegate something (the tableView). You can do this with code or with Storyboard. With Storyboard just control-click from the tableView to the ViewController and select “delegate”. Now the AlertView will work perfectly.

        tom

        • Joe Hoffman says:

          Hi Tom,

          Seems as though you figured it out! I’ll have to take a look at this an update accordingly. I’m not sure if I forgot it or if iOS 6 didn’t require a delegate to display an alert. Also, FYI , you don’t have to click and drag. You can also create a property for the UIAlertview and set the delegate like: alertView.delegate = self.

  4. Hey i tried the tutorial with xcode 5.x. So i simulated on ios7 device. It worked only for the tableview part. The UIAlertView did not show up. Maybe you can explain why? Would be great! Thanks.

  5. To all of you struggling solution is in missing brackets. Code should look like this:

    – (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
    UIAlertView *viewCreatureInfo = nil;
    NSString *creatureInfo = nil;

    switch (indexPath.section) {
    case bugsSection:
    creatureInfo = [[NSString alloc] initWithFormat:@”Name:%@ Scientific Name: %@”, [self.bugs objectAtIndex:indexPath.row],[self.scientificBugs objectAtIndex:indexPath.row] ];
    break;
    case animalsSection:
    creatureInfo = [[NSString alloc] initWithFormat:@”Name:%@ Scientific Name: %@”, [self.Animals objectAtIndex:indexPath.row],[self.scientificAnimals objectAtIndex:indexPath.row] ];
    break;
    }
    viewCreatureInfo = [[UIAlertView alloc] initWithTitle: @”Creature” message:creatureInfo delegate: nil cancelButtonTitle:@”OK” otherButtonTitles:nil];

    [viewCreatureInfo show];
    }

    • Joe Hoffman says:

      Thanks John,

      I updates the article to reflect this. Not sure how those brackets could have ever gone missing?

Trackbacks

  1. […] Visit site: iOS Programming Recipe 11: Using The UITableView Part II […]

Speak Your Mind

*

css.php
Privacy Policy