Drill down table view with a detail view

Since I published my drill down table view tutorial, I got a lot of positive feedback but with one common request. How do I load a different detail view for an item or how do I load a UITabBarController in the detail view. In this tutorial I will show how to load a different detail view.

Introduction
Using a drill down table view we can display hierarchical data where the last view is responsible of displaying some detail information. This view in which the detail information is displayed can be the same for all the data in the table view (like my last tutorial) or it can be different based on the path that the user took. In this tutorial I will show you how to add a different detail view based on the path the user took to get to the last item. This tutorial is based on the UITableView – Drill down table view tutorial and borrows its source code.

This is how one of the detail view looks like
Loading a detail view
Let’s take a step back and think about the browser and the HTML it renders. The browser doesn’t know what it is going to display and it does not even know the style it should set for the HTML. All that information is present in the HTML and the browser simply renders everything on the screen.

Does this mean that to display different data, I need different views even though the view looks the same? No, if the view looks the same you can reuse the same view again to display different data.

Let us think about the navigation controller as the browser and the data in the plist file as the HTML. If we want to tell the browser to style certain elements of the page, we would put that information in the HTML. In a similar fashion, we can put the detail view information in the plist file which the navigation controller will display. You only need to set this information in the last node of the plist file. When it is time to display the detail view, the code can look at this field and show the right detail view to the user.

Let us change the plist file from the last tutorial to include some information about the detail view.

Changing the plist file
Here we will add a new item to the last child; the title that gets displayed before the detail view is shown. The item will have a title of “View”, type will be number and the value will be 1, 2, or 3. Where 1 = display a UITabBarController in the detail view, 2 = display a detail view with an image in it, and 3 = display a simple detail view.

Your plist file should look like this for the four items

Recap
We have added an extra item at the end of called “View” whose type is integer and value is either 1, 2, or 3. Where 1 = load a detail view UITabBarController, 2 = load a detail view with an image and 3 = load a simple detail view. Let’s see how to add a detail view with a UITabBarController

Adding a UITabBarController to the detail view
Let’s add a UITabBarController to the detail view. We will need an outlet of type UITabBarController, so lets create that in the header file of RootViewController. The code changes like this

//RootViewController.h
@interface RootViewController : UITableViewController {

NSArray *tableDataSource;
NSString *CurrentTitle;
NSInteger CurrentLevel;
IBOutlet UITabBarController *tbController;
}

@property (nonatomic, retain) NSArray *tableDataSource;
@property (nonatomic, retain) NSString *CurrentTitle;
@property (nonatomic, readwrite) NSInteger CurrentLevel;

@end

The dealloc method changes like this

//RootViewController.m
- (void)dealloc {
[tbController release];
[CurrentTitle release];
[tableDataSource release];
[super dealloc];
}

Launch Interface Builder by double clicking “RootViewController.xib” file and drag and drop a UITabBarController in the nib file. Select File’s Owner and select the outlet “tbController” and drag it over to the UITabBarController and release. Now we have connected the outlet tbController to the UITabBarController. Add two views in the UITabBarController and change the title of the UITabBarItems to say “View One” and “View Two”. Also add a label to both the views which would say “View One” for the first and “View Two” for the second. This is all you have to do in the Interface Builder.

Display the detail view
We now have to display the UITabBarController as a detail view when an item is selected in the UITableView. Before we do that let’s stop for a while and see what we did. We added a UITabBarController in the same nib file as the RootViewController instead of creating a different nib file. When displaying a detail view, normally we would initialize the view controller and push it on top of the navigation controller. If we try to do the same here we would see the same table view again, which we do not want. The trick here is to switch the view of the RootViewController from the table view to the view of the tbController. Let’s see how to do this; the code for tableView:didSelectRowAtIndexPath changes like this

//RootViewController.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

//Get the dictionary of the selected data source.
NSDictionary *dictionary = [self.tableDataSource objectAtIndex:indexPath.row];

//Get the children of the present item.
NSArray *Children = [dictionary objectForKey:@"Children"];

if([Children count] == 0) {
NSInteger ViewNumber = [[dictionary objectForKey:@"View"] integerValue];
switch (ViewNumber) {
case 1: {
RootViewController *rvc = [[RootViewController alloc] initWithNibName:@"RootViewController" bundle:[NSBundle mainBundle]];
//Switch the view here
rvc.view = tbController.view;
[self.navigationController pushViewController:rvc animated:YES];
[rvc release];
}
break;
case 2:
break;
case 3: {
DetailViewController *dvController = [[DetailViewController alloc] initWithNibName:@"DetailView" bundle:[NSBundle mainBundle]];
[self.navigationController pushViewController:dvController animated:YES];
[dvController release];
}
break;
default: {
DetailViewController *dvController = [[DetailViewController alloc] initWithNibName:@"DetailView" bundle:[NSBundle mainBundle]];
[self.navigationController pushViewController:dvController animated:YES];
[dvController release];
}
break;
}
}
else {

//Prepare to tableview.
RootViewController *rvController = [[RootViewController alloc] initWithNibName:@"RootViewController" bundle:[NSBundle mainBundle]];

//Increment the Current View
rvController.CurrentLevel += 1;

//Set the title;
rvController.CurrentTitle = [dictionary objectForKey:@"Title"];

//Push the new table view on the stack
[self.navigationController pushViewController:rvController animated:YES];

rvController.tableDataSource = Children;

[rvController release];
}
}

That is the complete code listing for tableView:didSelectRowAtIndexPath method but it does a lot of things and we only need to concentrate on some of the code, so teh condensed version is listed below

//RootViewController.m - tableView:didSelectRowAtIndexPath
if([Children count] == 0) {
NSInteger ViewNumber = [[dictionary objectForKey:@"View"] integerValue];
switch (ViewNumber) {
case 1: {
RootViewController *rvc = [[RootViewController alloc] initWithNibName:@"RootViewController" bundle:[NSBundle mainBundle]];
//Switch the view here
rvc.view = tbController.view;
[self.navigationController pushViewController:rvc animated:YES];
[rvc release];
}
break;
case 2:
break;
case 3: {
DetailViewController *dvController = [[DetailViewController alloc] initWithNibName:@"DetailView" bundle:[NSBundle mainBundle]];
[self.navigationController pushViewController:dvController animated:YES];
[dvController release];
}
break;
default: {
DetailViewController *dvController = [[DetailViewController alloc] initWithNibName:@"DetailView" bundle:[NSBundle mainBundle]];
[self.navigationController pushViewController:dvController animated:YES];
[dvController release];
}
break;
}
}

The above code first checks if the present item has any children or not, if it doesn’t then we look for the value of the item “View”. If it is 1, we know that we have to display a detail view with a UITabBarController. As mentioned before we need to switch the view property of the controller to the view of the tab bar controller, which we do after initializing the controller. At last we ask the navigation controller to display our view which is connected to the controller.

Test it out to see how it works.

We also mentioned that if the “View” has a value of 3 then we would display the simple detail view. So drill down Item 3/4 and see the result. Don’t do that with Item 2 yet because it will not work.

Adding a detail view which will display an image.

Hopefully by now you have a clear idea of how to display different detail views when working with a drill down app.

To display a view with an image create a new view and a new view controller and name the view “ImageView” and the view controller “ImageViewController”. Open the ImageView in IB and set the class to be “ImageViewController” and connect the view outlet from the File’s Owner to the view. Drag and drop a UIImageView from the library on the view. We will set the image of this UIImageView when the view loads and the name of the image will be passed from the root view controller. Since we need to display an image in the image view from Xcode we need an outlet of type UIImageView in the Interface Builder. The header file of “ImageViewController” changes like this

//ImageViewController.m
@interface ImageViewController : UIViewController {

IBOutlet UIImageView *imgView;
NSString *ImageName;
}

@property (nonatomic, retain) NSString *ImageName;

@end

We have also declared a property called “ImageName” which the root view controller will use to pass the name of the image to the image view controller and the image view controller will display the image in the image view.

This is how the viewDidLoad method changes

//ImageViewController.m
- (void)viewDidLoad {
[super viewDidLoad];

NSString *Path = [[NSBundle mainBundle] bundlePath];
NSString *ImagePath = [Path stringByAppendingPathComponent:ImageName];
UIImage *tempImg = [[UIImage alloc] initWithContentsOfFile:ImagePath];
[imgView setImage:tempImg];
[tempImg release];
}

The dealloc method looks like this

//ImageViewController.m
- (void)dealloc {
[ImageName release];
[imgView release];
[super dealloc];
}

Displaying the detail view
Let’s go back to the RootViewController.m file and import “ImageViewController.h” file at the top and it should look like this

//RootViewController.m
#import "RootViewController.h"
#import "DrillDownAppAppDelegate.h"
#import "DetailViewController.h"
#import "ImageViewController.h"

@implementation RootViewController
...

In tableView:didSelectRowAtIndexPath we will now display the image detail view when the “ViewNumber” is set to 2. This is how the code looks like

//RootViewController.m - tableView:didSelectRowAtIndexPath method
case 2: {
ImageViewController *ivc = [[ImageViewController alloc] initWithNibName:@"ImageView" bundle:[NSBundle mainBundle]];
ivc.ImageName = @"Image.jpg";
[self.navigationController pushViewController:ivc animated:YES];
[ivc release];
}
break;

In the above code we initialize the “ImageViewController” and set the name of the image that it should display and ask the navigation controller to display it. Do not forget to place an image in the “Resources” folder.

Build and Run to see how it works.

Conclusion
I hope this helps you in some way on how to load a detail view with a UITabBarController. If you have any questions on loading a different detail view please take a look at this tutorial. Comments are most welcome and if you have any questions please send me an email at iphonearticles[@]gmail[dot]com.

Happy Programming,
iPhone SDK Articles


Attachments

Suggested Readings

Leave a Reply

Your email address will not be published. Required fields are marked *