UISplitViewController state restoration in iOS 8

…衆ロ難τιáo~ 提交于 2019-12-03 20:06:50

I solved this by subclassing UISplitViewController and overriding - (void)decodeRestorableStateWithCoder:(NSCoder *)coder to do nothing. This way the split view controller does not have an opportunity to restore its views, but its child view controllers still participate in state restoration.

The first thing needed when implementing UI State Restoration is changing from using didFinishLaunchingWithOptions to willFinishLaunchingWithOptions. If you set the delegate now in the willFinish the collapse will be called as expected. The issue was likely the delegate was set too late and it had already collapsed without your special handling.

Another issue is that the restoration paths to the controllers are different when in landscape and portrait so could be getting in a weird state. Because of the change it can't automatically find the existing detail view controller and creates new instances and either both or one of them likely get thrown away by the split view delegate because of misconfiguration of the detail item. In the State Restoration docs under "Recreate Your View Controllers" on step 3 it says it looks for an already created view controller with the same path which sadly fails when restoring after an orientation/trait change because the path is different. So it falls back to step 4 and creates a brand new empty misconfigured detail controller and is the reason you see the wrong configuration of controllers.

To understand the restoration identifier paths, implement application:viewControllerWithRestorationIdentifierPath:coder: in the app delegate and output the path components you'll see in portrait the last to be restored looks like:

SplitViewController,
MasterNavigationController,
DetailNavigationController,
DetailViewController

...which corresponds the single hierarchy primary of the split view controller (note: DetailNavigationController is a hidden nested navigation controller in this configuration).

And in landscape the last two to be restored are:

SplitViewController,
MasterNavigationController,
MasterViewController

and

SplitViewController,
DetailNavigationController,
DetailViewController

...which corresponds to the primary and secondary controller hierarchies of the split view.

So now knowing the restoration path to DetailViewController can be different, you can understand that if you try to automatically restore a portrait path while the storyboard has initlaised in landscape it won't find that detail view controller and resort to creating a new one. So I think the solution is to give it a helping hand to find it regardless of how the restoration path was saved:

- (UIViewController *)application:(UIApplication *)application viewControllerWithRestorationIdentifierPath:(NSArray *)identifierComponents coder:(NSCoder *)coder{
    if([identifierComponents.lastObject isEqualToString:@"DetailViewController"]){
        UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
            UINavigationController *secondaryNavigationController = splitViewController.viewControllers.lastObject;;
            DetailViewController *detail = (DetailViewController *)secondaryNavigationController.viewControllers.firstObject;
        return detail;
    }
    return nil;
}

Now the restoration will correctly use the existing detail controller which is configured correctly and it won't be thrown away by the split view delegate which was resulting in you being left with the master.

Another way this issue can manifest is seeing two detail controllers pushed onto the navigation stack after restoration, which is what happens if you force the split view delegate to not throw away the initial detail controller, and when the restoration creates another one you end up with two pushed on!

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!