Build the tab-bar from a plist
Working in a team, I often find myself trying to avoid using Interface Builder (IB) as much as possible. This is because trying to resolve the inevitable conflicts that arise when two developers change the same xib
can be soul-destroying ⚰️. A sea of seemingly unrelated xml
changes awaits anyone who dares to attempt to manually resolve xib
conflicts, making it almost impossible to understand what the other developer actually changed, never mind reason why those changes have been made.
In this article, I will look at how we can avoid using IB
to configure a UITabBarController
instance by moving its configuration into a simple to understand plist
file.
Plist Structure
<plist version="1.0">
<array>
<dict>
<key>TabBarTitle</key>
<string>HOME_TAB</string>
<key>TabBarImage</key>
<string>tabbar_home_icon.png</string>
<key>RootViewController</key>
<string>HomeViewController</string>
</dict>
</array>
</plist>
The above plist
structure is used to define the indiviudal tabs of a tab-bar. Each tab has three properties:
TabBarTitle
TabBarImage
RootViewController
TabBarTitle
is used as the tab's title, Image
is used for tab's icon and RootViewController
will used to instantiate an instance of UIViewController
that will become that tab's view-controller.
If two developers happen to change the above plist
file, it should be trivial to see what the changes are and figure out how to resolve them.
In the below example, the above
plist
file is calledApplicationHierarchy
.
Building the tabs
-(void)createApplicationHierarchy {
NSString *applicationHierarchyPlistFilePath = [[NSBundle mainBundle] pathForResource:@"ApplicationHierarchy" ofType:@"plist"];
if (applicationHierarchyPlistFilePath != nil) {
NSArray *applicationHierarchy = [[NSArray arrayWithContentsOfFile:applicationHierarchyPlistFilePath] retain];
NSMutableArray *navigationElements = [[[NSMutableArray alloc]init] autorelease];
for (int x = 0; x < [applicationHierarchy count]; x++) {
NSDictionary *tabbarDictionary = [applicationHierarchy objectAtIndex:x];
//Create tabbar's element's root viewcontroller
Class rootViewControllerClass = NSClassFromString ([tabbarDictionary objectForKey:@"RootViewController"]);
id rootViewController = [[rootViewControllerClass alloc]init];
UINavigationController *tabRootViewNavigationController = [[UINavigationController alloc] initWithRootViewController:rootViewController];
[rootViewController release];
//Give tab bar element a title
NSString *tabBarTitle = NSLocalizedString([tabbarDictionary objectForKey:@"TabBarTitle"], @"");
//Give tab bar element an image
UIImage *tabBarImage = [UIImage imageNamed:[tabbarDictionary objectForKey:@"TabBarImage"]];
//create tabbar item and pass it configuration defined aboce
UITabBarItem *tabBarItem = [[UITabBarItem alloc] initWithTitle:tabBarTitle image:tabBarImage tag:x];
tabRootViewNavigationController.tabBarItem = tabBarItem;
[tabBarItem release];
[navigationElements addObject:tabRootViewNavigationController];
[tabRootViewNavigationController release];
}
[applicationHierarchy release];
self.tabBarController.viewControllers = [NSArray arrayWithArray:navigationElements];
}
}
In the above method, we load the contents of the ApplicationHierarchy
plist
into an array, iterate through that array instantiating UIViewController
instances and configuring UITabBarItem
instances based in the extracted plist
values. Once all tabs have been configured, we then assign the view-controllers to be the tab-bar's view-controllers.