Search code examples
iphoneobjective-cuinavigationcontrolleruitabbarcontrollerobject-state

save and restore state of a tab bar controller


I have an application that has a UITabBarController with two tabs, each having its own navigation controller. Now I want to store the state of the application when the user closes it, so that when the user relauches the application will show the same place as the last time before it was closed.
So, in applicationWillTerminate: I have

[NSKeyedArchiver archiveRootObject:tabBarController toFile:@"lastVisitedTab"];

Then, in applicationDidFinishLaunching: I have

UITabBarController *last= (UITabBarController *)[NSKeyedUnarchiver unarchiveObjectWithFile:@"lastVisitedTab"];
if (last)
    tabBarController = [last retain];

I also have an extension to UIImage to make it compliant to NSCoding. However, this doesn't work, as the state is not preserved. The first tab gets selected all the time, and no navigation is preserved either.
Can someone tell me what's wrong, or show me how to do it correctly?


Solution

  • I figured out how to do it finally, thanks to Felixyz's idea. Below is what I have to do to store tabs, regardless of their data. If, says, a view is loaded with data downloaded from an URL, store the URL instead of the whole view. You would have to override

    - (void)encodeWithCoder:(NSCoder *)encoder
    - (id)initWithCoder:(NSCoder *)decoder
    

    in your UIViewController subclass to tell the view controller to save appropriate data before the application stops.
    Now in your application delegate save the data before quiting

    - (void)applicationWillTerminate:(UIApplication *)application
        // data buffer for archiving
        NSMutableData *data = [NSMutableData data];
        NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
        // the index of selected tab
        [archiver encodeInt:tabBarController.selectedIndex forKey:@"TAB_INDEX"];
        // array of keys for each navigation controller, here I have 3 navigation controllers
        NSArray *keys = [NSArray arrayWithObjects:
                         @"NAVIGATION_CONTROLLER_1",
                         @"NAVIGATION_CONTROLLER_2",
                         @"NAVIGATION_CONTROLLER_3", nil];
        for (int i = 0; i < keys.count; i++) {
            UINavigationController *controller = [tabBarController.viewControllers objectAtIndex:i];
            NSMutableArray *subControllers = [NSMutableArray arrayWithArray:controller.viewControllers];
            // the first view controller would already be on the view controller stack and should be removed
            [subControllers removeObjectAtIndex:0];
            // for each of the navigation controllers save its view controllers, except for the first one (root)
            [archiver encodeObject:subControllers forKey:[keys objectAtIndex:i]];
        }
        [archiver finishEncoding];
        // write that out to file
        NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
        [data writeToFile:[documentsDirectory stringByAppendingPathComponent:@"ARCHIVE_PATH"] atomically:YES];
    }
    

    And then, when relaunching

    - (void)applicationDidFinishLaunching:(UIApplication *)application {
        // set up the tabs
        tabBarController = [[UITabBarController alloc] init];
        tabBarController.viewControllers = [NSArray arrayWithObjects:
                                            [[[UINavigationController alloc] initWithRootViewController:rootViewController1] autorelease],
                                            [[[UINavigationController alloc] initWithRootViewController:rootViewController2] autorelease],
                                            [[[UINavigationController alloc] initWithRootViewController:rootViewController3] autorelease], nil];
        // look for saved data, if any
        NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
        NSData *archive = [NSData dataWithContentsOfFile:[documentsDirectory stringByAppendingPathComponent:@"ARCHIVE_PATH"]];
        // if no data found, skip this step
        if (archive) {
            NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:archive];
            // set the tab
            tabBarController.selectedIndex = [unarchiver decodeIntForKey:@"TAB_INDEX"];
            NSArray *keys = [NSArray arrayWithObjects:
                             @"NAVIGATION_CONTROLLER_1",
                             @"NAVIGATION_CONTROLLER_2",
                             @"NAVIGATION_CONTROLLER_3", nil];
            // push view controllers up the stack
            for (int i = 0; i < keys.count; i++) {
                NSArray *controllers = [unarchiver decodeObjectForKey:[keys objectAtIndex:i]];
                for (UIViewController *controller in controllers) {
                    [((UINavigationController *)[tabBarController.viewControllers objectAtIndex:i]) pushViewController:controller animated:NO];
                }
            }
        }
        // Add the tab bar controller's current view as a subview of the window
        [window addSubview:tabBarController.view];
    }