Aleksandar • Vacić

iOS bits and pieces

State restoration for modal view controllers

In my first post about state restoration on iOS 6 (or newer), I explained how you should use restorationIdentifier for the view controllers that are part of the on-load UI (and thus created in AppDelegate) and restorationClass to create and return controllers which are deeper in navigation stack.

If you do that, UIKit will take care of restoring the UITabBarController and UINavigationController and you just deal with view controllers beyond the topViewController inside the navigation controller hierarchy.

That’s still a good general guideline. However, as is usually the case, there are exceptions.

Restoring modal navigation controllers

One typical case is this: you tap a button which modally presents UIViewController embedded in UINavigationController. I have two instances of this in my currency converter iPhone app, Banca:

“Both of these buttons open modal UINC ”

This is typical code that opens the modal NC:

1
2
3
4
5
RTAboutViewController *vc = [[RTAboutViewController alloc] init];
UINavigationController *nc = [[UINavigationController alloc] initWithRootViewController:vc];
nc.restorationIdentifier = @"AboutNC";

[self presentViewController:nc animated:YES completion:nil];

RTAboutViewController has its own restorationClass to take care of its restoration, but what/how restores its parent UINavigationController? At first I thought I need to subclass UINC and add restorationClass but quickly realized that’s stupid; no way Apple devs would leave that as only solution.
Of course they didn’t.

It’s the AppDelegate that does it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
- (UIViewController *)application:(UIApplication *)application
viewControllerWithRestorationIdentifierPath:(NSArray *)identifierComponents
coder:(NSCoder *)coder {

//   return various navigation controllers here.
//   actual view controlers will each be returned in their own classes

NSString *lastIdentifier = [identifierComponents lastObject];
if ([lastIdentifier isEqualToString:@"MainNC"]) {
  return self.window.rootViewController;

} else if ([lastIdentifier isEqualToString:@"AboutNC"]) {
  UINavigationController *nc = [[UINavigationController alloc] init];
  nc.restorationIdentifier = @"AboutNC";
  return nc;

} else if ([lastIdentifier isEqualToString:@"CurrenciesNC"]) {
  UINavigationController *nc = [[UINavigationController alloc] init];
  nc.restorationIdentifier = @"CurrenciesNC";
  return nc;
}

return nil;
}

Easy and works exactly as you expect it to.