57

您可以将子视图控制器关联到 Storyboard 中的自定义容器视图控制器吗?

我可以将子视图控制器链接到选项卡视图控制器,并且可以将一个视图控制器链接到导航控制器。

我必须对容器 VC 做什么才能接受子 VC?

4

5 回答 5

76

作为 Caleb 和 Matt 的答案的组合,我做到了:

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if ([segue.identifier isEqualToString:@"cpdc_check_embed"]) {
        self.checkVC = segue.destinationViewController;
    }
}

...checkVC容器控制器上的属性在哪里:

@property (weak,nonatomic) PXPCheckViewController * checkVC;

您只需将embedsegue设置Storyboard ID为您想要的任何内容(在本例中为cpdc_check_embed):

检查 Xcode 中的嵌入屏幕

...然后检查-prepareForSegue:sender:.

仍然不是一个出口,但比马特(恕我直言)更干净,比迦勒更具体,你仍然会得到一个漂亮的故事板:

在此处输入图像描述

于 2012-09-05T15:04:44.943 回答
15

故事板很好地处理了内置的容器视图控制器,向子/根视图控制器显示 segue,以便清楚地显示关系。将子视图控制器和父视图控制器分成不同的场景也很好。

如果你想在自己的项目中实现这种效果,那么有一个技巧并不完美,但很简单。在我的示例中,假设我有一个容器视图控制器,它就像一个标签栏控制器,只有两个标签,“左”和“右”。我想让一个场景代表父视图控制器,两个单独的场景代表“左”子视图控制器和“右”子视图控制器。

即使不可能,如果我可以IBOutlet在不同场景中创建从容器视图控制器到其子级的 s,然后在显示我的容器视图控制器时根据UIViewController描述的规则设置父/子关系,那就太好了文档。如果我们引用了我们的“左”和“右”子视图控制器,那么我们可以毫无问题地设置关系。

这个引用问题的标准解决方案是通过将Object出口拖入容器视图控制器的场景中来创建对子视图控制器的引用,然后将它们的类类型指定为子视图控制器类的实例。

然而,为了让孩子在不同的场景(如 Apple 的内置容器)中保持分离,我们将使用不同的技巧。首先,假设我们在容器类中声明了以下属性ContainerViewController

@property (nonatomic, strong, readwrite) UIViewController *leftViewController;
@property (nonatomic, strong, readwrite) UIViewController *rightViewController;

在我们的故事板中,选择代表“左”视图控制器的场景。在属性检查器中,将视图控制器的identifier属性设置为"cvc_leftViewController"(“cvc_”指的是ContainerViewController,但实际上标识符可以是您想要的任何东西)。对右视图控制器的场景执行相同操作,将其标识符设置为"cvc_rightViewController".

现在将以下代码插入到ContainerViewController'sviewDidLoad方法中:

if (self.storyboard) {
    _leftViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"cvc_leftViewController"];
    _rightViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"cvc_rightViewController"];
}

ContainerViewController情节提要加载时,它将从各自的场景中获取“左”和“右”视图控制器,并通过其属性设置对它们的引用。现在您已经控制了子视图控制器实例,您可以随意设置父/子关系。要了解如何正确执行此操作,请参阅UIViewController 文档

这个技巧并不完美,并且有很多警告,但如果你小心,你可以让它在你的项目中很好地工作。

编辑:虽然这完全没有必要,也没有任何意义,但如果你真的想让故事板显示连接从你的容器到你的子视图控制器,就像 Apple 的内置容器一样,只需使用我上面的方法,然后设置直接在容器场景和子场景之间进行转场,并且永远不会执行这些转场。现在一切都会正常工作并且看起来也很漂亮。

于 2012-07-03T18:09:34.950 回答
7

您可以将子视图控制器关联到 Storyboard 中的自定义容器视图控制器吗?

我认为您在这里要问的是如何将一个场景中的视图控制器连接到不同场景中的视图控制器的插座。我不相信这是可能的,也许是因为情节提要机器可能不会同时加载情节提要中的所有场景。

您可能会问这个问题,因为您想在从一个场景切换到下一个场景时将一些信息从一个视图控制器传递到另一个视图控制器。使用情节提要时执行此操作的方法是-prepareForSegue:sender:在一个或两个受 segue 影响的视图控制器中覆盖。参数中提供的 UIStoryboardSegue 对象seguesourceViewControllerdestinationViewController属性,也有identifier属性。您可以使用这些属性来识别即将在视图控制器之间传输数据的 segue。

Ray Wenderlich 的博客有一个很好的关于使用故事板的两部分教程,可能会对您有所帮助:

  • 第 1 部分包括设置故事板项目、添加场景和创建转场。

  • 第 2 部分涉及使用 segue 在场景之间进行过渡,包括prepareForSeque上面提到的方法。

iOS 5 允许多个视图控制器在同一场景中处于活动状态(尽管仍然应该由一个控制器负责),因此情节提要中的单个场景可能有多个控制器。您可以使用插座将这些控制器相互连接,并且您可以像在 IB 中一样配置这些连接:在同一场景中从一个控制器拖动到另一个控制器。通常的插座列表将弹出,让您选择要连接的插座。

于 2011-11-22T20:24:51.923 回答
5

在一个场景中使用多个控制器的关键(我相信你在这里)是使用 IB 中对象列表中的神秘对象来表示另一个视图控制器并连接它的插座。

这个答案How to create custom view controller container using storyboard in iOS 5应该对我有所帮助。答案还提供了一个非常有用的工作示例应用程序。

于 2011-11-23T04:29:08.900 回答
5

@Ben 的(否则是合理的)答案的问题在于它只适用于一级嵌套。除此之外,还需要定制每个后续 VC 以将嵌套视图控制器保存在 prepareForSegue 中。

为了解决这个问题,我花了太多时间探索一个基于 NSObject 的索引,您可以将它添加到 Storyboard、绑定到场景,然后根据类型和 restoreId 在全局索引中注册它的父 VC。这行得通/可以行得通,但最终付出了太多努力,并且仍然需要视觉绑定和以编程方式查找的两步过程。

对我来说,最简单和最通用的解决方案是懒惰地降低视图控制器层次结构

在我的简单测试项目中,我在 viewDidLoad 中添加了以下几行:

    self.left.data = [
        "Zombie ipsum reversus ab viral inferno, nam rick grimes malum cerebro.",
        "De carne lumbering animata corpora quaeritis." ]

其中left定义为:

lazy var left:CollectionViewController = { [unowned self] in
    return self.childViewControllerWithId("Left") as! CollectionViewController }()

childViewControllerWithId定义为:

extension UIViewController {
    func childViewControllerWithId(rid:String) -> UIViewController? {

        // check immediate child controllers
        for vc in self.childViewControllers as! [UIViewController] {
            if vc.restorationIdentifier == rid { return vc }
        }

        // check nested controllers
        for vc in self.childViewControllers as! [UIViewController] {
            if let vc = vc.childViewControllerWithId(rid) {
                return vc
            }
        }

        assert(false, "check your assumptions")
        return nil
    }
}

请注意find,如果需要,您可以根据类型进行其他变体。另请注意,以上要求您在 Storyboard 文件中定义恢复 ID。如果您没有相同视图控制器的重复实例,那么使用类型会更容易。

为了说明希望显而易见的事情,您不需要实现 prepareForSegue,也不必使用延迟加载,您只需调用find(...).

于 2015-03-13T23:43:40.210 回答