iPhone 6+状态恢复,主分割视图中带有标签栏

时间:2015-02-24 13:00:32

标签: ios uisplitviewcontroller iphone-6-plus state-restoration

我在iPhone 6 +中支持状态恢复时遇到问题。

这是我的等级制度: enter image description here

问题是当纵向方向发生状态恢复,然后在横向方向上进行状态解码尝试。

说明: 在纵向模式下,只有TabBar作为主视图(事实上,细节视图不存在),因此它被推入TabBar导航的控制器。

然后,稍后,当app尝试为横向执行状态恢复时,我的详细信息视图将被推送到主导航控制器(当它应该在详细信息中时)。

由于自定义层次结构,我已相应地实现了UISplitViewControllerDelegate方法,并且它们工作正常。 UISplitViewControllerDelegate方法还确保状态恢复在以下情况下起作用:

Landscape -> Landscape
Landscape -> Portrait
 Portrait -> Portrait

什么不起作用的是:肖像 - >因为,正如我所说,代理方法在处于未折叠状态时不会被调用,因此视图层次结构不知道如何从主视图中分割细节并将其嵌入到详细的导航控制器中。

3 个答案:

答案 0 :(得分:0)

我不确定我是否完全理解您的问题,因为iPhone 6+上的从纵向到横向应该从折叠状态变为分裂状态,因此应该调用此代理:

- (UIViewController *)splitViewController:(UISplitViewController *)splitViewController separateSecondaryViewControllerFromPrimaryViewController:(UIViewController *)primaryViewController

但是你似乎暗示这没有被调用,而在Portrait中它处于非崩溃状态?

在我使用的拆分视图控制器上,我发现最好让控制器为自己排序。如果您为详细视图使用showDetail segues,它应该为您处理拆分。您是否确保使用showDetail segues从Controller1-> 4推送DetailController而不是显示segues?

因此,假设您正在使用正确的segue到详细信息,只需在委托中返回nil,让分割视图控制器自行排序?

答案 1 :(得分:0)

到目前为止,我已经解决了这个问题:在viewWillAppear中,所有细节控制器都是从标签栏的导航控制器中剪切出来的。 存储状态时,会保存设备方向和SplitViewController.viewControllers(对我而言,它不会像在iOS7上那样自动存储它们。)

- (void) encodeRestorableStateWithCoder:(NSCoder*)coder
{
    [super encodeRestorableStateWithCoder:coder];
    [coder encodeObject:self.viewControllers forKey:@"viewControllers"];
    [coder encodeInteger:[UIApplication sharedApplication].statusBarOrientation forKey:@"orientation"];
}

- (void) decodeRestorableStateWithCoder:(NSCoder*)coder
{
    [super decodeRestorableStateWithCoder:coder];
    NSArray* viewControllers = [coder decodeObjectForKey:@"viewControllers"];
    if (viewControllers.count > 0)
    {
        self.viewControllers = viewControllers;
    }
    restoredOrientation = (UIInterfaceOrientation) [coder decodeIntegerForKey:@"orientation"];
}

这里是viewWillAppear实现:

- (void) viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    // iPhone6+: if state restoration happened while in portrait orientation and app is launched while in landscape,
    // then all detail views should be cut from master view and split details view is set appropriately
    if (firstLoad &&
            [UIScreen mainScreen].scale > 2.9 && // assure it's iPhone 6+
            UIInterfaceOrientationIsPortrait(restoredOrientation) &&
            UIInterfaceOrientationIsLandscape(UIApplication.sharedApplication.statusBarOrientation))
    {
        UITabBarController* tbc = self.viewControllers[0];
        NSArray* detachedControllers = [tbc cutControllersFrom:DocumentViewController.class];
        if (detachedControllers.count > 0)
        {
            UINavigationController* documentNavigation = [self.storyboard
                    instantiateViewControllerWithIdentifier:@"NavigationController"];
            documentNavigation.viewControllers = detachedControllers;
            self.viewControllers = @[ self.viewControllers[0], documentNavigation ];
        }
        else // place some default no-selection controller in detail
        {
            UINavigationController* noSelectionNavigation = [self.storyboard
                    instantiateViewControllerWithIdentifier:@"NoSelectionSID"];
            self.viewControllers = @[ self.viewControllers.firstObject, noSelectionNavigation ];
        }
    }
    firstLoad = NO;
}

其中cutControllersFrom方法是UITabBarController上的一个类别:

- (NSArray*) cutControllersFrom:(Class)controllerClass
{
    NSArray* ret;
    for (UIViewController* vc in self.viewControllers)
    {
        if (![vc isKindOfClass:UINavigationController.class])
        {
            continue;
        }

        UINavigationController* nc = (UINavigationController*) vc;
        NSArray* removed = [nc cutFrom:controllerClass];
        if (vc == self.selectedViewController)
        {
            ret = removed;
        }
    }

    return ret;
}

调用cutFrom:方法,它是UINavigationController上的一个类别:

- (NSArray*) cutFrom:(Class)controllerClass
{
    NSMutableArray* toRemove = [NSMutableArray array];
    BOOL startRemoving = NO;
    UIViewController* endingViewController;

    for (NSUInteger i = 0; i < self.viewControllers.count; i++)
    {
        UIViewController* vc = self.viewControllers[i];
        if ([vc isKindOfClass:controllerClass])
        {
            startRemoving = YES;
            endingViewController = self.viewControllers[i - 1];
        }

        if (startRemoving)
        {
            [toRemove addObject:vc];
        }
    }
    if (endingViewController)
    {
        [self popToViewController:endingViewController animated:NO];
    }

    return toRemove;
}

答案 2 :(得分:0)

当状态保留为纵向时,恢复标识符路径将不同于预期的风景,这是使用拆分视图控制器时的默认初始状态。即细节位于主要位置,而不是次要位置。因此,当应用重新启动时,它将无法重新创建以前的层次结构,并且将诉诸于在其单独的配置中重新创建那些控制器(About the UI Restoration Process中的步骤4),这意味着separateSecondaryViewControllerFromPrimaryViewController将不会之所以调用,是因为控件已经分开。您可能会注意到,在启动时创建了两个详细信息控制器,第一个可能设置了默认属性的详细信息控制器被丢弃了,或者您甚至可能看到两个详细信息控制器被推入导航堆栈。我不确定为什么要设法使Portrait-> Portrait正常工作,因为它应该遇到相同的问题。

要解决这些问题,您可以实现application:viewControllerWithRestorationIdentifierPath:coder,它将在纵向层次结构中调用(在Portrait-> Landscape场景中),并且可以通过在已加载的层次结构中搜索现有的视图控制器来返回它们从情节提要。恢复视图控制器后,它将检测到从保存的肖像到当前风景的方向已发生变化,并且将调用separateSecondaryViewControllerFromPrimaryViewController。但是,由于拆分控制器中有些状态未保存,例如_preservedDetailController(这是拆分视图控制器可以自动分离控制器的方式),因此我不确定这是否是正确的方法。在先前的崩溃之后),所以如果方向改变后我有100%的把握还不确定的话,我们有可能只是放弃状态而已。

这是我的测试应用程序中的一个示例:

- (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;
        NSURL *objectURI = [coder decodeObjectForKey:@"object"];
        if(objectURI){
            NSManagedObjectContext *moc = self.persistentContainer.viewContext;
            NSManagedObjectID *objectID = [moc.persistentStoreCoordinator managedObjectIDForURIRepresentation:objectURI];
            NSManagedObject *object = [moc objectWithID:objectID];
            detail.object = object;
        }
        // attempt to workaround a bug for _preservedDetailController not being restored.
        [splitViewController _willShowCollapsedDetailViewController:secondaryNavigationController inTargetController:nil];
        return detail;
    }
    else if([identifierComponents.lastObject isEqualToString:@"DetailNavigationController"]){
        UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
        UINavigationController *secondaryNavigationController = splitViewController.viewControllers.lastObject;
        return secondaryNavigationController;
    }
    return nil;
}