views:

424

answers:

4

So I have a stack of three UITableViewControllers, each of which displays its view correctly underneath the navigation bar when I tap through the UI manually.

However, now I'm working on restoring state across app restart, and so I'm pushing the same two controllers on top of the root view controller, one at a time, in the same method in the main thread. What ends up happening then is that the middle controller's view is laid out too far down, and the top controller's view is too far up (underneath the nav bar).

Relevant code:

for (int i = 0; i < [controllerState count]-1; i++) {
 MyViewController* top = (MyViewController*)navigationController.topViewController;
 int key = [[controllerState objectAtIndex:i] integerValue];
 [top restoreNextViewController:key]; // this calls pushViewController: 
 // so top will be different in the next iteration
}

I suspect that the problem is that I'm not allowing some important UI refresh process to take place in between the two pushes, because they happen in the same thread. It almost looks as though the automatic view adjustment that's supposed to affect the top controller affects the middle one instead.

Is that right? And if so, what should I do, since I do want all the state-restoration to take place immediately upon app start?

EDIT: looks like I was unclear. restoreNextViewController: is a MyViewController subclass method that restores the controller's state based on a stored key, and then pushes the appropriate child controller with [self.navigationController pushViewController:foo animated:NO]. I'm doing this because my actual app, unlike this simplified case, has up to 6 controllers in the stack, and they're not always the same ones. So I figured this would be a cleaner design than going down the stack checking controllers' classes. Each controller already knows how to push a child controller in response to user input; why not reuse that on app restart?

I'm not having any trouble getting the controllers to show up; they're just being laid out strangely.

A: 

You can call pushViewController:animated: multiple times. Please post the code for -restoreNextViewController. It's confusing why this code is as complex as it is. Have you read the section of the View Controller Guide on Creating a Nav Controller? They cover pushing view controllers on the stack at startup.

Rob Napier
I'm not having any trouble getting the controllers to show up; they're just being laid out wrong. See my edited question.
lawrence
A: 

I agree with Rob, I would look at implementing UINavigationController when your application is loaded. Then you would push each of your controllers onto UINavigtionController in the sequence you want them layered.

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated

So if you know what state you want to bring your application back to you could load the appropriate ViewController in the series to be latered and push them onto the NavigationController.

UIViewController *myViewController = [[UIViewController alloc] initWithNibName:@"myView" bundle:nil];
[navigationController pushViewController:myViewController animated:NO];

Then if you want to "pop" back to the UIViewController either the user can press the "Back" button on navigationbar for free. Or you can control the navigation with

- (UIViewController *)popViewControllerAnimated:(BOOL)animated

eg [[self navigationController] popViewControllerAnimated:NO];

You can also control whether you want the NavigationBar to show or not if you want to control the push/pop yourself. I hope I've explained this well enough I've just traveled this road recently myself.

Ben Clark-Robinson
A: 

I ended up manually resetting each table view's content inset during its controller's viewWillAppear:.

lawrence
+1  A: 

Have you considered having each view controller push its child during viewWillAppear:? I would just set an isRestoringState property in your appDelegate and check that during viewWillAppear:, if it's set, run your restore for that view, including pushing any visible child view. Something like:

- (void)viewWillAppear:(BOOL)animated {
    if ([[UIApplication sharedApplication] isRestoringState]) {
        // restore local state, set myChildController if a child is visible
        if (myChildController) {
            [self.navigationController pushViewController:myChildController animated:NO];
        }
    }
}
Frank Szczerba