views:

1401

answers:

2

Hi,

I have another question about restoring application state, on the iPhone. Simple data (like the selected tab) can be stored in NSUserDefaults, but it is not enough.

I want to restore the whole state, including the Navigation Controllers (go to the sub-sub-sub View Controller).

My problem is that my application is divided in several xib files, so at the beginning, all the View Controllers are not instantiated. Is there any way to "force" the instanciation from a xib file ?

(I have no code under the hand, but I can try to write a small end if it is not clear)

Thanks a lot.

+4  A: 

I do this in my app, Litho Graph. If you're on a page when you quit, then the next time you launch, it restores you to that page. It's pretty easy, too: in my main view controller's -viewDidLoad implementation, I check for the last page viewed key in the user defaults. If there's nothing, I continue as normal (and show a list of all pages). If there's something, I open that page (pushing the new view controller onto the navigation controller stack).

The actual loading of the view controllers is very straightforward: I just instantiate them. Each of my view controller classes has overridden -init to call -initWithNibName:bundle: on super, so calling +alloc and -init to create the view controller instance is enough to load it from the appropriate .xib file. Then, as described above, I push it onto the navigation controller's stack:

[self.navigationController pushViewController: controller animated: YES];

You can do this as many layers deep as you need to, of course, but bear in mind that you may want to push the view controllers without animation if there's going to be several transitions; it would be counterproductive for the user to have to wait through several animations (:

Rob Rix
+3  A: 

Calling [viewController view] will make sure that a given view controller's XIB is loaded; once it is, you can use any of its other properties normally.

Here's what I tend to do:

@class Record;  // some model object--I assume it has an integer record ID
@class DetailViewController;

@interface RootViewController : UIViewController {
    IBOutlet DetailViewController * detailController;
}

- (void)restore;
...
@end

@implementation RootViewController

// Note: all detailController showings--even ones from within 
// RootViewController--should go through this method.
- (void)showRecord:(Record*)record animated:(BOOL)animated {
    [self view];    // ensures detailController is loaded

    [[NSUserDefaults standardUserDefaults] setInteger:record.recordID 
                                               forKey:@"record"];
    detailController.record = record;

    [self.navigationController pushViewController:detailController 
                                         animated:animated];
}

- (void)restore {
    int recordID = [[NSUserDefaults standardUserDefaults] integerForKey:@"record"];

    if(recordID) {
        Record * record = [Record recordWithID:recordID];
        [rootViewController showRecord:record animated:NO];
        // If DetailViewController has its own state to restore, add this here:
        // [detailController restore];
    }
}
...
- (void)viewDidAppear:(BOOL)animated {
    [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"record"];
}
@end

@implementation MyAppDelegate

- (void)applicationDidFinishLaunching:(UIApplication*)application {
    ...
    [rootViewController restore];
    ...
}
...
@end

Each level of the view hierarchy takes care of its own state saving, so the app controller doesn't have to know about all the possible views and which orders they could be in.

Brent Royal-Gordon
It seems to be the perfect solution, I will test that as soon as possible. Thanks !
Rémy BOURGOIN
Loading of all controllers on startup breaks concept of loading them lazily, increasing application startup time and memory usage.
porneL
It doesn't load all controllers because the subcontroller is not loaded unless the supercontroller has a record ID for it.
Brent Royal-Gordon