将子视图控制器链接到故事板中的父视图控制器

时间:2011-11-20 01:14:18

标签: ios xcode cocoa-touch uiviewcontroller uistoryboard

您可以将子视图控制器与故事板中的自定义容器视图控制器关联吗?

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

我应该怎样对容器VC接受子VC?

5 个答案:

答案 0 :(得分:75)

作为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;

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

check embed screen in Xcode

...然后检查-prepareForSegue:sender:中的标识符。

仍然不是一个出口,但比马特(恕我直言)更清洁,比Caleb更具体,你仍然会看到一个漂亮的故事板:

enter image description here

答案 1 :(得分:15)

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

如果你想在你自己的项目中实现这个效果,那么有一个不完美但非常简单的技巧。在我的示例中,假设我有一个容器视图控制器,其作用类似于标签栏控制器,只有两个选项卡,“左”和“右”。我希望有一个场景代表父视图控制器,两个独立的场景代表'左'子视图控制器和'右'子视图控制器。

即使不可能,如果我可以在容器视图控制器中创建IBOutlet到不同场景中的子节点,然后在显示我的容器视图控制器时设置父/子,那将会很好根据{{​​3}}描述的规则建立关系。如果我们引用了“左”和“右”子视图控制器,那么我们可以设置关系没问题。

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

然而,为了让孩子们在像Apple的内置容器这样的不同场景中分开,我们将使用不同的技巧。首先,假设我们在容器类ContainerViewController中声明了以下属性:

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

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

现在将以下代码插入ContainerViewController的{​​{1}}方法:

viewDidLoad

从故事板加载if (self.storyboard) { _leftViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"cvc_leftViewController"]; _rightViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"cvc_rightViewController"]; } 时,它将从各自的场景中抓取“左”和“右”视图控制器,并通过其属性设置对它们的引用。现在您可以控制子视图控制器实例,您可以根据需要设置父/子关系。要了解如何正确执行此操作,请参阅UIViewController documentation

这个技巧并不完美,并且有许多警告,但如果你小心,你可以使它适合你的项目。

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

答案 2 :(得分:7)

  

是否可以将子视图控制器关联到自定义容器视图   故事板中的控制器?

我认为你在这里问的是如何将一个场景中的视图控制器连接到另一个场景中的视图控制器的插座。我不相信这是可能的,也许是因为故事板机器可能没有同时加载故事板中的所有场景。

你可能会问这个,因为当你从一个场景转移到另一个场景时,你想要将一些信息从一个视图控制器传递到另一个。在使用故事板时,这样做的方法是覆盖受segue影响的一个或两个视图控制器中的-prepareForSegue:sender:segue参数中提供的UIStoryboardSegue对象具有sourceViewControllerdestinationViewController属性,还有identifier属性。您可以使用这些属性来标识将在视图控制器之间传输数据的segue。

Ray Wenderlich的博客提供了一个很好的两部分关于使用可以帮助你的故事板的教程:

  • Part 1涵盖了设置故事板项目,添加场景和创建segues。

  • Part 2处理使用segues在场景之间转换,包括上面提到的prepareForSeque方法。

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

答案 3 :(得分:5)

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

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

答案 4 :(得分:4)

@ Ben(其他合理的)答案的问题是它只能在一个嵌套级别上运行。除此之外,还需要定制每个后续VC以将嵌套视图控制器保存在prepareForSegue中。

为了解决这个问题,我花了太多时间探索一个基于NSObject的索引,你可以将它添加到Storyboard,绑定到一个场景,然后在全局索引中注册它的父VC,基于type和restorationId。这可以工作/可以工作,但最终需要付出太多努力,仍然需要两个可视化绑定的过程,并以编程方式查找。

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

在我的简单测试项目中,我将以下行添加到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(...)