0

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

这是我的层次结构: 在此处输入图像描述

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

说明:竖屏模式下,只有TabBar作为主视图(实际上,详细视图并不存在),所以它被推到了TabBar导航的控制器中。

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

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

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

不起作用的是:Portrait -> Landscape,因为正如我所说,在未折叠状态时不会调用委托方法,因此视图层次结构不知道如何从主视图中拆分细节并将其嵌入到细节导航控制器中。

4

3 回答 3

1

到目前为止,我已经解决了这个问题:在 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;
}
于 2015-02-26T17:43:40.280 回答
0

不确定我是否完全理解您的问题,因为在 iPhone 6+ 上从纵向到横向应该从折叠状态变为拆分状态,所以应该调用这个委托:

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

但是,您似乎暗示这没有被调用,并且在 Portrait 中它处于非折叠状态?

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

因此,假设您对细节使用正确的 segues,只需在代表中返回 nil 以让拆分视图控制器自行排序?

于 2015-02-24T13:56:10.550 回答
0

当状态以纵向保留时,恢复标识符路径将不同于预期的横向,这是使用拆分视图控制器时的默认初始状态。即细节在主要而不是在次要中。因此,当应用程序重新启动时,它将无法重新创建以前的层次结构,并将诉诸于在其单独的配置中重新创建这些控制器(关于 UI 恢复过程中的第 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;
}
于 2018-08-08T11:15:45.060 回答