Nov 202012
 

My Ferret game is using a UITabBarController in part of the UI. Most of the sample code and tutorials I’ve seen have you start with the TabBar template, or have the Tab Bar in the main window. Ferret has a SwitchBoard type icon grid (implemented by Nimbus) and when you press one of the icons you push a view controller that has a tab bar at the bottom.

First time I did this I wrote some pretty awful code. I tried to do the “right” thing, doing the UI in Interface Builder, but I couldn’t hook up each of the Tab Bar’s views in IB, I had to do it programatically. See here?

tabbar

Notice in the Attributes Inspector that I do not have a field for the UIViewController’s nib!

So I wrote code like this:

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
        MeReadOnlyViewController *meViewController = [[MeReadOnlyViewController alloc] initWithNibName:@"MeReadOnlyViewController" bundle:nil];
        [meViewController setImagePickerVC:self];
		MeFotosViewController *fotosViewController = [[MeFotosViewController alloc] initWithNibName:@"MeFotosViewController" bundle:nil];
		MeBadgesViewController *badgesViewController = [[MeBadgesViewController alloc] initWithNibName:@"MeBadgesViewController" bundle:nil];
		MeFriendsViewController *friendsViewController = [[MeFriendsViewController alloc] initWithNibName:@"MeFriendsViewController" bundle:nil];

		NSArray *array = [[NSArray alloc] initWithObjects:meViewController, fotosViewController, badgesViewController, friendsViewController, nil];
		self.viewControllers = array;

        [self.view addSubview:meViewController.view];
		self.selectedViewController = meViewController;
    }
    return self;
}

Now, that’s not horrible. If you’re not building a UITabBarController from IB you’re going to have to do this. But check out that line,

self.viewControllers = array

What’s up with that?

@interface MeTabViewController : UIViewController <UINavigationControllerDelegate>

@property (nonatomic, retain) NSArray *viewControllers;
@property (nonatomic, retain) UIViewController *selectedViewController;

@property (strong, nonatomic) IBOutlet UITabBar *tabBar;
@property (strong, nonatomic) IBOutlet UITabBarItem *meTab;
@property (strong, nonatomic) IBOutlet UITabBarItem *badgesTab;
@property (strong, nonatomic) IBOutlet UITabBarItem *fotosTab;
@property (strong, nonatomic) IBOutlet UITabBarItem *friendsTab;

@end

Now that’s making me really squirm. Can you say “bad code smell?” I need to create my own NSArray of viewControllers, but why do I need a property for each of the UITabBarItems? Here’s why:

- (void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item
{
    UIViewController *newViewController;

    self.navigationItem.rightBarButtonItem = nil;

    if (item == meTab_) {
        newViewController = [viewControllers_ objectAtIndex:0];

        self.navigationItem.rightBarButtonItem = [self editButtonItem];
    }else if (item == fotosTab_) {
        newViewController = [viewControllers_ objectAtIndex:1];
    } else if (item == badgesTab_) {
        newViewController = [viewControllers_ objectAtIndex:2];
    }else if (item == friendsTab_) {
        newViewController = [viewControllers_ objectAtIndex:3];
    }

    [self.selectedViewController.view removeFromSuperview];
    [self.view addSubview:newViewController.view];

    self.selectedViewController = newViewController;
}

That’s gross. It works, it works just fine but it seems unmaintainable, fragile, and just bad. I building, by hand, a UIViewController which is really just a UITabBarController. So why not just make one of those? I tried in IB, but I couldn’t hook up the UITabBarController to the view. After struggling a bit, and a bit of google, I decided to just do it by hand, it isn’t that hard.

+ (UITabBarController *) createMeTabBarController
{
    //create a UITabBarController object
    UITabBarController *tabBarController=[[UITabBarController alloc]init];

    // create our view controllers
    MeReadOnlyViewController *meViewController = [[MeReadOnlyViewController alloc] initWithNibName:@"MeReadOnlyViewController" bundle:nil];
    meViewController.tabBarItem.image = [UIImage imageNamed:@"me-profile.png"];
    meViewController.title = @"Me";

    MeFotosViewController *fotosViewController = [[MeFotosViewController alloc] initWithNibName:@"MeFotosViewController" bundle:nil];
    fotosViewController.title = @"Fotos";
    fotosViewController.tabBarItem.image = [UIImage imageNamed:@"me-fotos.png"];

    MeBadgesViewController *badgesViewController = [[MeBadgesViewController alloc] initWithNibName:@"MeBadgesViewController" bundle:nil];
    badgesViewController.title = @"Badges";
    badgesViewController.tabBarItem.image = [UIImage imageNamed:@"me-badges.png"];

    MeFriendsViewController *friendsViewController = [[MeFriendsViewController alloc] initWithNibName:@"MeFriendsViewController" bundle:nil];
    friendsViewController.title = @"Friends";
    friendsViewController.tabBarItem.image = [UIImage imageNamed:@"me-friends.png"];

    tabBarController.viewControllers= [NSArray arrayWithObjects:meViewController, fotosViewController, badgesViewController, friendsViewController, nil];

This eliminates the MeTabViewController class and xib completely. Notice how similar the code is to the first code snippet I showed. I still need to build my array of UIViewControllers but now I must also set the icon and title for the UITabBarItems.