- Developing with Navigation Controllers and Split Views
- Recipe: Building a Simple Two-Item Menu
- Recipe: Adding a Segmented Control
- Recipe: Navigating Between View Controllers
- Recipe: Presenting a Custom Modal Information View
- Recipe: Page View Controllers
- Recipe: Scrubbing Pages in a Page View Controller
- Recipe: Tab Bars
- Recipe: Remembering Tab State
- Recipe: Building Split View Controllers
- Recipe: Creating Universal Split View - Navigation Apps
- Recipe: Custom Containers and Segues
- One More Thing: Interface Builder and Tab Bar Controllers
- Summary
Recipe: Remembering Tab State
On iOS, persistence is golden. When starting or resuming your application from termination or interruption, always return users to a state that closely matches where they left off. This lets your users pick up with whatever tasks they were involved with and provides a user interface that matches the previous session. Recipe 5-8 introduces an example of doing exactly that.
This recipe stores both the current tab order and the currently selected tab, and does so whenever those items are updated. When a user launches the application, the code searches for previous settings and applies them when they are found.
The approach used here depends on two delegate methods. The first, tabBarController:didEndCustomizingViewControllers: provides the current array of view controllers after the user has customized them with the More > Edit screen. This code captures their titles (10%, 20%, and so on) and uses that information to relate a name to each view controller.
The second delegate method is tabBarController:didSelectViewController:. The tab bar controller sends this method each time a user selects a new tab. By capturing the selectedIndex, this code stores the controller number relative to the current array.
Setting these values depends on using iOS’s built-in user defaults system, NSUserDefaults. This preferences system works very much as a large mutable dictionary. You can set values for keys using setObject:forKey:
[[NSUserDefaults standardUserDefaults] setObject:titles forKey:@"tabOrder"];
and retrieve them with objectForKey:.
NSArray *titles = [[NSUserDefaults standardUserDefaults] objectForKey:@"tabOrder"];
Always make sure to synchronize your settings as shown in this code to ensure that the defaults dictionary matches your changes. If you do not synchronize, the defaults may not get set until the application terminates. If you do synchronize, your changes are updated immediately. Any other parts of your application that rely on checking these settings will then be guaranteed to access the latest values.
When the application launches, it checks for previous settings for the last selected tab order and selected tab. If it finds them, it uses these to set up the tabs and select a tab to make active. Since the titles contain the information about what brightness value to show, this code converts the stored title from text to a number and divides that number by ten to send to the initialization function.
Most applications aren’t based on such a simple numeric system. Should you use titles to store your tab bar order, make sure you name your view controllers meaningfully and in a way that lets you match a view controller with the tab ordering.
Recipe 5-8 Storing Tab State to User Defaults
@implementation TestBedAppDelegate - (void)tabBarController:(UITabBarController *)tabBarController didEndCustomizingViewControllers:(NSArray *)viewControllers changed:(BOOL)changed { // Collect the view controller order NSMutableArray *titles = [NSMutableArray array]; for (UIViewController *vc in viewControllers) [titles addObject:vc.title]; [[NSUserDefaults standardUserDefaults] setObject:titles forKey:@"tabOrder"]; [[NSUserDefaults standardUserDefaults] synchronize]; } - (void)tabBarController:(UITabBarController *)controller didSelectViewController:(UIViewController *)viewController { // Store the selected tab NSNumber *tabNumber = [NSNumber numberWithInt: [controller selectedIndex]]; [[NSUserDefaults standardUserDefaults] setObject:tabNumber forKey:@"selectedTab"]; [[NSUserDefaults standardUserDefaults] synchronize]; } - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [application setStatusBarHidden:YES]; window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; // Globally use a black tint for nav bars [[UINavigationBar appearance] setTintColor:[UIColor blackColor]]; NSMutableArray *controllers = [NSMutableArray array]; NSArray *titles = [[NSUserDefaults standardUserDefaults] objectForKey:@"tabOrder"]; if (titles) { // titles retrieved from user defaults for (NSString *theTitle in titles) { BrightnessController *controller = [BrightnessController controllerWithBrightness: ([theTitle intValue] / 10)]; UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:controller]; nav.navigationBar.barStyle = UIBarStyleBlackTranslucent; [controllers addObject:nav]; } } else { // generate all new controllers for (int i = 0; i <= 10; i++) { BrightnessController *controller = [BrightnessController controllerWithBrightness:i]; UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:controller]; nav.navigationBar.barStyle = UIBarStyleBlackTranslucent; [controllers addObject:nav]; } } tabBarController = [[RotatingTabController alloc] init]; tabBarController.viewControllers = controllers; tabBarController.customizableViewControllers = controllers; tabBarController.delegate = self; // Restore any previously selected tab NSNumber *tabNumber = [[NSUserDefaults standardUserDefaults] objectForKey:@"selectedTab"]; if (tabNumber) tabBarController.selectedIndex = [tabNumber intValue]; window.rootViewController = tabBarController; [window makeKeyAndVisible]; return YES; } @end