I love the iPhone Twitter client Tweetie - it has a really excellent navigation framework that makes it fast to navigate between multiple Twitter accounts.
I'm developing an iPhone app that logs actions against one or more persons and thought that Tweetie's navigation style would suit. Tweetie uses a navigation controller to handle the hierarchical nature of browsing account/tweets/users but also uses a tab bar to navigate between various views - a user's tweets, their mentions, DMs etc. Investigating the Apple documentation, having a tab bar controller within a navigation controller is not a
supported Apple user interface approach - the iPhone SDK doesn’t support it and trying results in a blank tab bar when you navigate to the view hosting it. I then
found this post, with a comment from Tweetie developer himself,
@atebits, alluding to the fact that a custom tab view controller solution is required.
The general steps are outlined below and full source code that can be used as a template for your own projects is posted at
GitHub. I recommend you download this now for reference while working through this article.
The App Delegate
The App Delegate holds the navigation controller - this controller is at the root of the view tree and contains any other view controller that is pushed onto it. The navigation bar at the top of the screen that handles the navigation back and forth between views is to a large extent, managed automatically by the navigation controller.
As discussed previously, the issue with current versions of the SDK is that
a tab bar controller cannot be pushed onto a navigation controller.
Now, have a look at MainWindow.xib in Interface Builder.
Note that the navigation controller has a “root view controller”. This is the controller that handles the first view that is pushed onto the navigation controller. Opening
RootViewController.h reveals that the controller is inherited from UITableViewController. It is fairly typical that the navigation controller oversees multiple levels of table views.
@implementation BabyBookAppDelegate
@synthesize window;
@synthesize navigationController;
The Root View Controller
In Tweetie, the root view controller is the list of Twitter accounts. In my sample code, each table cell is driven from a simple array of strings (people’s names) for demonstration purposes.
self.title = @"Accounts";
NSArray *array = [[NSArray alloc] initWithObjects:@"Jack", @"Jill", @"John", nil];
self.accounts = array;
[array release];
When a cell is clicked, the delegate method
didSelectRowAtIndexPath is executed. This pushes the custom tab view controller onto the navigation controllers stack. Note, that this is a custom tab view controller, and does not inherit from the typical SDK class UITabViewController. This means that there is some extra work to manually build this view controller, and also the view it owns.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self.tabViewController setTitle:[accounts objectAtIndex:indexPath.row]];
[self.navigationController pushViewController:self.tabViewController animated:YES];
}
The UICustomTabViewController
Open
TabViewController.xib. Have a look at the structure of the interface. I've manually created a Tab Bar, which in this case contains the two default tab bar items -
Favourites and
More. These two buttons have associated view controllers that own the view that appears when a button is clicked. The UICustomTabViewController class manages the switching of the views by maintaining (a) an array of the view controllers and (b) a reference to the selected view controller.
The controller also acts as a delegate for the tab bar, so can respond to taps on the tab bar items through the
didSelectItem delegate method. Based on the item tapped, a reference to the appropriate view controller (Favourites or More) is pulled from the array of view controllers. The current view is removed from the superview and the
view of the selected view controller is then added to the UICustomTabViewController’s view. Really, the view above the tab bar is getting swapped out for a new view, depending on which tab bar item is tapped.
- (void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item
{
if (item == favouritesTabBarItem) {
UIViewController *fabViewController = [viewControllers objectAtIndex:0];
[self.selectedViewController.view removeFromSuperview];
[self.view addSubview:fabViewController.view];
self.selectedViewController = fabViewController;
} else if (item == moreTabBarItem) {
UIViewController *moreViewController = [viewControllers objectAtIndex:1];
[self.selectedViewController.view removeFromSuperview];
[self.view addSubview:moreViewController.view];
self.selectedViewController = moreViewController;
}
)
The More Tab
The
MoreTabViewController.m class controls another table view and demonstrates that the navigation controller can be used to manage further views in a hierarchy. The delegate method
didSelectRowAtIndexPath will retrieve the navigation controller from the application delegate and push the view controller that handles the next view in the navigation hierarchy -
MoreOptionViewController.
NavTabAppDelegate *delegate = [[UIApplication sharedApplication] delegate];
[delegate.navigationController pushViewController:moreOptionViewController animated:YES];
[moreOptionViewController.label setText:msg];
Again, the full source for the NavTab project is on
GitHub.
Further recommended reading -
Apple View Controller Programming Guide