Question

I am trying to incorporate State Restoration in my app. I have it working fine for the most part, but presenting a navigation controller for a modal view on top of another navigation controller seems challenging.

For testing, I created a new split-view app on the iPad, with navigation controllers for both sides of the split view, and a Master and Detail view controller for each side, the roots of their respective navcontrollers. In the master view, you can click on a button to push a new TestViewController onto the navController stack programatically. I hook up the splitView in the storyboard, add restorationIDs to everything, opt-in to the delegate, provide a restoration class and adhere to the UIViewControllerRestoration protocol for TestViewController (since it's created programmatically) and everything works fine. If I close the app and retort it, it will start the TestViewController pushed onto the master's navcontroller. So far so good.

I then change the button handler to present the TestViewController inside a new UINavigationController, present it onto the master's navigation controller, to show a modal view (instead of pushing it on the nav stack). Now, when I relaunch the app, there is no modal view there anymore. TestModalViewController's viewControllerWithRestorationIdentifierPath:coder: is actually called correctly as before, but the modal view is never presented for some reason.

Here is the code for what I'm talking about

MasterViewController.h:

- (void)pushButton:(id)sender
{
    TestModalViewController *test = [[TestModalViewController alloc] initWithNibName:@"TestViewController" bundle:nil];
    test.restorationIdentifier = @"testid";
    test.restorationClass = [TestModalViewController class];

    UINavigationController *modal = [[UINavigationController alloc] initWithRootViewController:test];
    modal.modalPresentationStyle = UIModalPresentationFormSheet;
    modal.restorationIdentifier = @"ModalTestID";
    [self.navigationController presentViewController:modal animated:YES completion:nil];
    return;
}

TestModalViewController.m:

+ (UIViewController *) viewControllerWithRestorationIdentifierPath:(NSArray *)identifierComponents coder:(NSCoder *)coder {
    TestModalViewController *test = [[TestModalViewController alloc] initWithNibName:@"TestViewController" bundle:nil];
    test.restorationClass = [TestModalViewController class];
    test.restorationIdentifier = [identifierComponents lastObject];
    return test;
}

Perhaps the UINavigationController that is created to display modally is never preserved? Not sure why, because it does have a restorationIdentifier.

Edit:

After further testing, it turns out if I remove the UINavigationController from the the pushButton: code, and present the TestModalViewController instance directly, it gets restored correctly. So something about the UINavigationController being presented from another UINavigationController?

This works (though not what I really want):

- (void)pushButton:(id)sender
{
    TestModalViewController *test = [[TestModalViewController alloc] initWithNibName:@"TestViewController" bundle:nil];
    test.restorationIdentifier = @"testid";
    test.restorationClass = [TestModalViewController class];

    //UINavigationController *modal = [[UINavigationController alloc] initWithRootViewController:test];
    //modal.modalPresentationStyle = UIModalPresentationFormSheet;
    //modal.restorationIdentifier = @"ModalTestID";
    [self.navigationController presentViewController:test animated:YES completion:nil];
    return;
}

EDIT: Attached link to test project: dropbox.com/sh/w8herpy2djjl1kw/vw_ZWqimgt

It's basically the Core Data master-detail template; run it on the iPad simulator. The + button in Master invokes the TestModalVC; if you then press the Home button, then kill debugger and launch again, you see the snapshot contains the TestModalVC but when the app is launched, it doesn't get restored

Was it helpful?

Solution

You can either create your own restoration class to handle this, or add the following to your app delegate:

- (UIViewController *)application:(UIApplication *)application viewControllerWithRestorationIdentifierPath:(NSArray *)identifierComponents
coder:(NSCoder *)coder
{
    NSString *lastIdentifier = [identifierComponents lastObject];
    if ([lastIdentifier isEqualToString:@"ModalTestID"])
    {
        UINavigationController *nc = [[UINavigationController alloc] init];
        nc.restorationIdentifier = @"ModalTestID";
        return nc;
    }
    else if(...) //Other navigation controllers
    {
    }

    return nil;
}

More information in the documentation.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top