I have an app that is not working properly with state restoration. It previously did, but as I started moving away from the storyboard it stopped.
My app starts with a LoginViewController
that is the starting view controller in my storyboard. If the login is successful, then it tries to add two FolderViewController
to a navigation controller. This is so that the visible folder is one level deep already. This is done in the following code:
UINavigationController *foldersController = [[UINavigationController alloc] initWithNavigationBarClass:nil toolbarClass:nil];
foldersController.restorationIdentifier = @"FolderNavigationController";
FolderViewController *root = [storyboard instantiateViewControllerWithIdentifier:@"FolderView"];
root.folderId = 0;
FolderViewController *fvc = [storyboard instantiateViewControllerWithIdentifier:@"FolderView"];
fvc.folderId = 1;
[foldersController setViewControllers:@[root, fvc] animated:YES];
[self presentViewController:foldersController animated:YES completion:nil];
The FolderViewController
has this awakeFromNib
- (void)awakeFromNib
{
[super awakeFromNib];
self.restorationClass = [self class]; // If we don't have this, then viewControllerWithRestorationIdentifierPath won't be called.
}
Within the storyboard the FolderViewController has a restorationIdentifier
set. When I press the Home button, the app is suspended. My restoration calls in FolderViewController
are being called:
// This is being called
- (void)encodeRestorableStateWithCoder:(NSCoder *)coder
{
[super encodeRestorableStateWithCoder:coder];
[coder encodeInt64:self.folderId forKey:@"folderId"];
}
The problem now is when I try and restore. I stop the app in my debugger and then start it up again. This kicks off the restoration process.
First, my viewControllerWithRestorationIdentifierPath:coder:
for my LoginViewController
is called. This doesn't do much, and its use is optional. I've tried removing it and I don't have any ill effect.
+ (UIViewController*) viewControllerWithRestorationIdentifierPath:(NSArray *)identifierComponents coder:(NSCoder *)coder
{
LoginViewController* vc;
UIStoryboard* sb = [coder decodeObjectForKey:UIStateRestorationViewControllerStoryboardKey];
if (sb)
{
vc = (LoginViewController *)[sb instantiateViewControllerWithIdentifier:@"LoginViewController"];
vc.restorationIdentifier = [identifierComponents lastObject];
vc.restorationClass = [LoginViewController class];
}
return vc;
}
Next, the viewControllerWithRestorationIdentifierPath:coder:
for my FolderViewController
is called:
// This is being called
+ (UIViewController*) viewControllerWithRestorationIdentifierPath:(NSArray *)identifierComponents coder:(NSCoder *)coder
{
FolderViewController* vc;
UIStoryboard* sb = [coder decodeObjectForKey:UIStateRestorationViewControllerStoryboardKey];
if (sb)
{
vc = (FolderViewController *)[sb instantiateViewControllerWithIdentifier:@"FolderView"];
vc.restorationIdentifier = [identifierComponents lastObject];
vc.restorationClass = [FolderViewController class];
vc.folderId = [coder decodeInt32ForKey:@"folderId"];
}
return vc;
}
I've previously had a decodeRestorableStateWithCoder:
as well, and it did get called. However, since it's setup in the viewControllerWithRestorationIdentifierPath:coder:
, it wasn't necessary to keep it around.
All of these things are being called the appropriate number of times. But in the end, the only view controller that is displayed in the LoginViewController
. Why are my FolderViewController
s not being displayed. Is there a missing setup that I need to do in my LoginViewController
to attach the view controllers that I manually added previously?
Edit
After reading http://aplus.rs/2013/state-restoration-for-modal-view-controllers/ which seemed relevant, I added the following code to the App delegate:
- (UIViewController *)application:(UIApplication *)application viewControllerWithRestorationIdentifierPath:(NSArray *)identifierComponents coder:(NSCoder *)coder
{
if ([identifierComponents.lastObject isEqualToString:@"FolderNavigationController"])
{
UINavigationController *nc = [[UINavigationController alloc] init];
nc.restorationIdentifier = @"FolderNavigationController";
return nc;
}
else
return nil;
}
I think the App is happier now, but it still isn't restoring properly. Now I get this error in my log:
Warning: Attempt to present <UINavigationController: 0xbaacf50> on <LoginViewController: 0xbaa1260> whose view is not in the window hierarchy!
It's something different.