I was recently set to write a hierarchical menu system for iOS app. The idea was to quickly add necessary functionality like user session handling and simple help system without designing a lot of dedicated pages. Yes, some applications feature witty login and registration pages but when it’s not the most significant part of the application maybe you could start with something more conventional.
So the idea was to use UITableView in grouped mode to show menu items and UINavigationController to move between pages back and forth. The expected result should look like this:

Typical approach would be to use UITableViewController for each menu page, but that has two major drawbacks:
- You have to write some boilerplate code for each page
- It’s a pain to enforce consistency among menu items
The second point means that if you want to change a look of cells you have to do it in all page controller classes. And we all know that it is better to have one place for such changes. The solution then is to think what is common for all pages and try to factor out as much as possible.
The first observation is that view controller for all menu pages could be the same and they all differ by implementation of table’s data source and delegate. So we introduce a MenuProvider class which extends NSObject and implements UITableViewDataSource and UITableViewDelegate protocols. For every page we will create an instance of common MenuViewController and parametrize it with a particular MenuProvider instance.

Now we notice that in our menu we have certain cell categories:
- Navigation cell: it has chevron at the right side, main text and maybe detail text. On touch it opens some other menu page.
- Text entry cell: used to ask user for some text like email and password.
- Action cell: works as a button.
Since we have to create instances of these cells in all our MenuProviders it makes sense to add factory methods to the base MenuProvider class to do so. Now we have a single place where we could change how all menu cells of a certain type look like.
typedef void (^MenuNavigator)(NSString *destination);
@interface MenuProvider : NSObject
<UITableViewDataSource, UITableViewDelegate, UITextFieldDelegate>
@property(nonatomic, readonly) NSString *title;
@property(nonatomic, strong) MenuNavigator navigator;
- (UITableViewCell *)tableView:(UITableView *)tableView
navigationCellWithText:(NSString *)text
detailText:(NSString *)detailText
destination:(NSString *)destination;
- (UITableViewCell *)tableView:(UITableView *)tableView
actionCellWithText:(NSString *)text
textAlignment:(UITextAlignment)textAlignment
selectionStyle:(UITableViewCellSelectionStyle)selectionStyle;
- (BAEditableCell *)tableView:(UITableView *)tableView
textEntryCellWithText:(NSString *)text;
@end
Yet another thing that we could factor out is navigation. We have to implement tableView:didSelectRowAtIndexPath: for each provider and when a navigation cell is tapped create MenuViewController, specific MenuProvider and push them to the navigation controller. The same sequence for all such cells—obvious target for refactoring. My solution is to parametrize each navigation cell with a certain 'destination'—identifier of a menu page that should be opened. Then we could add a generic piece of code to handle navigation requests.

One interesting addition is the help pages. App bundles several static html files each covering a particular help topic. When you navigate to a help topic menu creates instance of MenuTextViewController which loads an html file into the UIWebView. And in order to specify which file to load we use the same 'destination' property. Now when the navigation cell wants to load the next page it first tries to create a MenuProvider associated with its 'destination' property. If it can’t create MenuProvider it tries to load html file with the name specified by 'destination' property.
Here is the method that makes a menu page accordingly to the discussed principles:
+ (MenuViewController *)makeMenuControllerForDestination:(NSString *)destination {
MenuProvider *provider = [self makeProviderForDestination:destination];
if (!provider) {
return nil;
}
MenuViewController *menuController = [[MenuViewController alloc] init];
menuController.contentSizeForViewInPopover = kContentSize;
menuController.provider = provider;
__unsafe_unretained MenuViewController *weakMenuController = menuController;
provider.navigator = ^(NSString *destination) {
if ([@"back" isEqualToString:destination]) {
[weakMenuController.navigationController popViewControllerAnimated:YES];
return;
}
MenuViewController *menuController =
[self makeMenuControllerForDestination:destination];
if (menuController) {
[weakMenuController.navigationController pushViewController:menuController
animated:YES];
} else {
MenuTextViewController *textController =
[self makeTextControllerForFile:destination];
if (textController) {
[weakMenuController.navigationController
pushViewController:textController animated:YES];
}
}
};
return menuController;
}
The source code is available on github. I encourage you to look at MenuHomeProvider, MenuLoginProvider, MenuRegistrationProvider and MenuHelpProvider classes and see for yourself how slim and clear they are. At the same time all power of table’s data source and delegate callbacks are available to you to customizes the pages as needed.