The purpose of a UITableView is to display a list of items, from which a user can select one item to see it’s detail view. The navigation that happens between the table view and the detail view is controlled by the navigation controller. In this tutorial we will see how to navigate to a detail view.

Introduction
In this tutorial, you will learn how to navigate to the detail view and also pass some data at the same time. This is the second tutorial in the UITableView tutorial series and inherits its source code from the first tutorial.

Creating a detail view
Open Interface Builder and click on File -> New -> (select Cocoa Touch) View, save it in the application directory and name it “DetailView”. You will be asked to add the view to the current project, click on “Add”. You may need to drag the view (in XCode) to the “Resources” folder. Now that you have your view, we will create a view controller class to control the view on the screen. In XCode select Classes then click on File -> New File -> (under iPhone OS) select UIViewController subclass and name it “DetailViewController, do not change the extension. Now we have to connect the view to the view controller we just created. In Interface Builder, select File’s Owner and open Identity Inspector, under class Identity set the class to “DetailViewController”, open Connections Inspector and create a connection from the view property to the view object in the nib file.

Now add controls to the view which will display the detail contents. The controls that you may want to add to the view, depends on the data you want to display. We will display the country selected in the table view, so a simple label should do. Drag and drop the label on the view. We need some way to change the text of the label from XCode, create a variable of type UILabel in xcode and connect it with the label object on the view. The label should be declared with IBOutlet property, so it shows up in the Connections Inspector. This is how the code looks like

//DetailViewController.h
#import <UIKit/UIKit.h>
 
@interface DetailViewController : UIViewController {
 
IBOutlet UILabel *lblText;
}
 
@end
 
//Dealloc method declared in DetailViewController.m
- (void)dealloc {
 
[lblText release];
[super dealloc];
}

After you have declared the variable, open IB and connect the variable to the label placed on the view in Connections Inspector. Now we can change the label’s properties from XCode.

Navigate to the detail view
The method tableView:didSelectRowAtIndexPath is called when a row is selected, it passes the tableview object with the indexPath object to tell us which row was selected. First import the “DetailViewController” class in RootViewController, so it knows about it. The following code will initialize the detail view and display it

//RootViewController.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
 
DetailViewController *dvController = [[DetailViewController alloc] initWithNibName:@"DetailView" bundle:[NSBundle mainBundle]];
[self.navigationController pushViewController:dvController animated:YES];
[dvController release];
dvController = nil;
}

A “DetailViewController” is created, initialized with initWithNibName:bundle message and the name of the nib file is passed as the parameter. The view controller is then push to the top of the stack with its animated property set to YES. At last, we clean up memory by releasing the detail view controller. Run the application and now you can select a row to see the detail view.

Passing data
We still have to pass the selected country from the list to the detail view. To do this, we will declare a property in “DetailViewController” whose data type is the same as the in the array, in our case NSString. This is what you have to do if you want to pass data from one view controller to another. The following code declares a property in “DetailViewController”

//DetailViewController.h
#import <UIKit/UIKit.h>
 
@interface DetailViewController : UIViewController {
 
IBOutlet UILabel *lblText;
NSString *selectedCountry;
}
 
@property (nonatomic, retain) NSString *selectedCountry;
 
@end
 
//Dealloc method declared in DetailViewController.m
- (void)dealloc {
 
[selectedCountry release];
[lblText release];
[super dealloc];
}
 
//First three lines of DetailViewController.m
#import "DetailViewController.h"
 
@implementation DetailViewController
 
@synthesize selectedCountry;

The property is synthesized at the top after the implementation begins. Now we can pass the selected country from the table view to the detail view. The tableView:didSelectRowAtIndexPath method looks like this

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
 
//Get the selected country
NSString *selectedCountry = [listOfItems objectAtIndex:indexPath.row];
 
//Initialize the detail view controller and display it.
DetailViewController *dvController = [[DetailViewController alloc] initWithNibName:@"DetailView" bundle:[NSBundle mainBundle]];
dvController.selectedCountry = selectedCountry;
[self.navigationController pushViewController:dvController animated:YES];
[dvController release];
dvController = nil;
}

We first get the selected country from the array, initialize the detail view controller, set the selected country to the property on the detail view controller and display it.

Setting the accessory view
Run the app and now we are able to select a row in a table view. However, it is not obvious to the user that a row can be selected to see its detail view. We can add a “accessory view” to the cell which will show up at the right end of the row. The accessory view can be set up in tableView:cellForRowAtIndexPath method or in tableView:accessoryTypeForRowWithIndexPath. We will use the later method to keep our code simple. This is how the source code changes

//RootViewController.m
- (UITableViewCellAccessoryType)tableView:(UITableView *)tableView accessoryTypeForRowWithIndexPath:(NSIndexPath *)indexPath {
 
//return UITableViewCellAccessoryDetailDisclosureButton;
return UITableViewCellAccessoryDisclosureIndicator;
}

The above method returns an enum UITableViewCellAccessoryType and we can return four values: UITableViewCellAccessoryNone, UITableViewCellAccessoryDisclosureIndicator, UITableViewCellAccessoryDetailDisclosureButton, and UITableViewCellAccessoryCheckmark. You can test the code by returning one of the four values to see how the accessory view looks like. If you return “UITableViewCellAccessoryDetailDisclosureButton” clicking on the button will not do anything, since the cell is not selected but a button is clicked. The SDK does provide a method which gets called when the accessory button is clicked and that is called tableView:accessoryButtonTappedForRowWithIndexPath. In this method we can call tableView:didSelectRowAtIndexPath which will load the detail view and this is how the code will look like

//RootViewController.m
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath {
 
[self tableView:tableView didSelectRowAtIndexPath:indexPath];
}

The last thing we need to do in detail view controller, is to display the selected country on the label. Do it in viewDidLoad method and this is how the code looks like

// DetailViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
 
//Display the selected country.
lblText.text = selectedCountry;
 
//Set the title of the navigation bar
self.navigationItem.title = @"Selected Country";
}

The method “viewDidLoad” gets called when the view is loaded, where we set the selected country by setting the text property of the label “lblText”. The title of the navigation bar is also set in the same method.

Conclusion
We have seen how to display a list of items in a table view, how to select a row and display the detail view, and in the next tutorial we will look at how to search the list of items in a table view. I hope you found this tutorial helpful and if you have any questions, please send me an email. Don’t forget to leave a comment.

Happy Programming,
iPhone SDK Articles


Attachments

Suggested Readings

 

28 Responses to UITableView – Loading a detail view

  1. Ziya Bal says:

    Hi,

    First of all, thanks for this superb tutorial, i think you helped the whole world with these :D .

    My Question about the DetailView. On click it will go to the DetailView and it wil show you “You selected %d” but can we do something else with that. On click i want to show another element, like an UILabel with an unique text about the Cell i clicked. Maybe with an image or something else to fill the DetailView.

    Thank you with your help!
    Greetings from Holland

  2. brandon says:

    Thanks! Your tutorials are simple to understand, and helped a lot with understanding these basic concepts!

  3. Matteo says:

    The app works, but the first detail, is not loaded. the second one i click, will be loaded, but the first won’t.
    this is the code:

    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    DiarioAppDelegate *appDelegate = (DiarioAppDelegate *)[[UIApplication sharedApplication] delegate];
    NSInteger row = [indexPath row];
    if (self.materiaViewController == nil) {
    MateriaViewController *aMateriaDetail = [[MateriaViewController alloc] initWithNibName:@"Materia" bundle:nil];
    self.materiaViewController = aMateriaDetail;
    [aMateriaDetail release];
    }
    Materia *materiaobj = [appDelegate.materiaArray objectAtIndex:row];
    materiaViewController.Descrizione.text = ( @"%@", materiaobj.descrizione );
    materiaViewController.title = ( @"%@", materiaobj.mat_name);
    [appDelegate.compitiNavController pushViewController:materiaViewController animated:YES];
    }

  4. Gary Cobb says:

    I can’t figure out what I am doing wrong. I try to run and get errors.
    ‘DetailViewController undeclared’
    ‘dvController undeclared’
    ‘ Method definition not in @implementation context’

    Please help.
    Thank you.

  5. mfoxxofm says:

    >I have the same Problem as Thiago when I try to program the code from ground up. Clicking on the cell throws an exception.

    Your code works fine, when I load it, though.

  6. Alain says:

    >Very good tuto ! It explains perfectly how using row selection :)

    If we use a UITextField instead of the UILabel, how can I set the new value typed by the user to the original TableView ? Thanks in advance !

  7. Anonymous says:

    >Hi Jai,

    This is the 3rd of your tutorials I am doing – please continue the great work!

    I have two comments:

    1. As Will stated twice above, I also cannot get the label to display the selected item. I can’t see any difference in the code between mine and I just downloaded yours, so it must be in the IB connection???

    2. Also I am wondering about the “flicker” at runtime. I just downloaded your project and it has the same “flicker”. Jai, do you this issue as well?

    Much thanks,
    ~Ralf

  8. danilkazzz says:

    >Thanks for this great tutorial, this is exactly what I was looking for!

    I got stuck at Navigate to the detail view. I just started a new project and made everything from the beginning. And finally I realized that the DetailView has to be created in IB, not Xcode.

    So folks, never give up.

    And thanks again!

  9. mcarey says:

    >Thanks for this tutorial! Wondering if you can help with my problem integrating your information on passing data. Code & explanation below:
    //////////////////////////////
    UIViewController *targetViewController = [[menuList objectAtIndex: indexPath.row] objectForKey:kViewControllerKey];
    if (targetViewController == nil)
    {
    …..
    targetViewController = [[RootViewController alloc] initWithNibName:@"FlipPageMainWindow" bundle:nil];
    targetViewController.curPageIndex = curPageIndex;
    …..
    [[self navigationController] pushViewController:targetViewController animated:YES];

    /////////////// ellipses above indicate other code not relevant to this problem

    curPageIndex is defined in RootViewController.h:
    @interface RootViewController : UIViewController {
    NSInteger curPageIndex;
    }

    @property (nonatomic) NSInteger curPageIndex;
    //////////////////////////////

    curPageIndex is synthesized in RootViewController.m:
    @implementation RootViewController

    @synthesize curPageIndex;
    //////////////////////////////

    YET, I am getting "error: request for member 'curPageIndex' in something not a structure or a union.
    Does the fact that I'm passing an Integer variable rather than a String have anything to do with it? I should think it wouldn't but integer vars do seem to need to be treated differently (in declaration, no asterisk.. no dealloc…)

    Thanks for any light you can shed on this problem!

  10. shawn says:

    >after creating detail view and detail view controller, I couldn’t get it to push the view until I added #import “DetailViewController.h” to the top of RootViewController.m … it was complaining ‘DetailViewController’ and ‘dvController’ were undeclared (first use in this function)

  11. Anonymous says:

    >Just want to say these tutorials are fantastic! I’m totally new to OS X/X code/Objective C/etc and was really struggling to figure out how to wire everything up using interface builder but this has been a tremendous help.

    I look forward to reading many more tutorials :) Thanks again!

  12. dylan says:

    >This is such a great site and series I will definitely link from my blog. I’ve started to follow you on twitter too (hope your getting better!).

  13. Anonymous says:

    >First off all, thanks you so much for your help.
    It is much appreciated and i understand the time that you put aside to help others. So Thank You!

    My Q’s are:

    - How to add a tab along the bottom so that the user can access other sections of the app

    - How to input different content in the detail view for each row?? So that there is a individual detail view for each selection..

    I know that you have previously posted this, however I am very new to this and dont quite understand your explanation.

    Any help would be appreciated;.
    Thanks a heap.

    Will

  14. Anonymous says:

    >Your tutorials have helped me enormously….so thank you verry much.
    I have a couple of questions though which i would really appreciate some help with. The first is that I would like to add a tab along the bottom of the page so that there can be other sections….and the second is how to change the information in the detail view for each row that can be selected..??

    I noticed that you have mentioned this in previous posts but I’m really new to this and didn’t quite understand….

    Thanks So much for your help.

    Will

  15. vishay says:

    >My mistake, those lines of code I posted execute to completion, but the program still throws an unexpected exception sometime after that.

  16. vishay says:

    >Hi Jai,

    Thanks for the helpful tutorials. I’m trying to get the DetailViewController to load but I am getting this error inside DVC’s initWithNibName method:
    initWithNibName:
    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
    // Initialization code
    }
    return self;

    Error:
    *** Terminating app due to uncaught exception ‘NSInternalInconsistencyException’, reason: ‘-[UIViewController loadView] loaded the “Detail View” nib but no view was set.’

    Thanks,
    Vishay

  17. iPhone SDK Articles says:

    >You can use this code to load a detail view

    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    DetailViewController *dvController = [[DetailViewController alloc] initWithNibName:@”DetailView” bundle:[NSBundle mainBundle]];
    pushViewController:dvController animated:YES];
    [dvController release];
    dvController = nil;
    }

  18. iPhone SDK Articles says:

    >@Phil

    Double click on DetailView.xib file to launch the Interface builder and Select the File’s Owner object and open Identity Inspector to check that the class under class identity is set to “DetailViewController”. Once you do that the variable will show up in the Connections Inspector.

    Please let me know if this helps or not

    Happy Programming,
    iPhone SDK Articles

  19. Anonymous says:

    >whats the code to make it so when a row is selected to goes to another xib(NIB) file.

    Thanks,
    James

  20. Phil says:

    >This doesnt seem to be working for me. When I try to connect the label to the lblText outlet, the lblText outlet doesnt show up in connection inspector. My only choice is “view”. Yes, I have added the line

    IBOutlet UILabel *lblText;

    to the code in DetailViewController.h, and I set the file’s owner to DetailViewController for the DetailView nib, but the label outlet doesnt show up.

  21. iPhone SDK Articles says:

    >@Pierre I am working on a tutorial which will do that. You can follow me on twitter to get the latest news on the site.

    http://www.iphonearticles.com/iphonearticles

    Happy Programming,
    iPhone SDK Articles

  22. Anonymous says:

    >Ur tutorial is very nice for newbies.
    I am an newbie..
    I have implemented the same code as u have said in this tutorial. The program is build completely without errors or warnings
    but, I could not see the label displaying the content i have selected.
    Hope !!I dont understand the connecting IB to Xcode could u please explain me what i should do in order to get an proper output

    Thank you very much in advance
    Bye
    Srikavi

  23. Pierre says:

    >Hello ! Good tuto ^^
    Is there a way to display in the detail View another TableView ? Thanks ^^

  24. iPhone SDK Articles says:

    >@Hugo Glad you enjoyed the tutorials.

    You can control which rows are clickable in tableView:willSelectRowAtIndexPath method. In this method you can find out which row will be selected and return nil, if you do not want it to be selected.

    If you want to load different detail views based on which row is clicked, you can do this in tableView:didSelectRowAtIndexPath method. Check which row is selected and based on that you can initialize and load a detail view.

    I hope this helps.

    Please let me know if I can be of any more help.

    Happy Programming,
    iPhone SDK Articles

  25. SeanOC says:

    >@Thiago

    I was getting an error at a similar point. The error basically said that it couldn’t import CoreGraphics.

    After poking around for a bit I realized that I hadn’t saved my changes in Interface Builder. Make sure that you’ve connected everything properly in Interface Builder and that you have the “.xib” file in your “resources” directory.

  26. Hugo says:

    >Thanks a lot for your articles, they are very helpful. I’ve been trying unsuccessfully to modify your code so that only some rows are clickable and lead to a detail view, while the other rows do not (for example, the cells of these rows only have an on/off switch control).
    Also, I would like to have several different detail views, and depending on which row is clicked, to slide in one or the other detail view. Is there any possibility that you could show that in another article?

    Thanks so much,
    Hugo

  27. iPhone SDK Articles says:

    >@Thiago

    Can you tell me what the error message is?

    Check if you have imported “DetailViewController” header file.

    Happy Programming,
    iPhone SDK Articles

  28. Thiago says:

    >I’m trying to work through this tutorial, but I’m stuck at “Navigate to the detail view”. When I try to run the app I get an uncaught exception at line: [self.navigationController pushViewController:dvController animated:YES];

    I’m able to launch the application but I get the exception when clicking on a row.

    Any ideas?

    Thiago