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

57

你能在Storyboard中将子视图控制器关联到自定义容器视图控制器吗?

我可以将子视图控制器链接到标签视图控制器,也可以将一个视图控制器链接到导航控制器。

如何使容器视图控制器接受子视图控制器?

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;
你只需要将embed转场的Storyboard ID设置为你想要的任何名称(在这种情况下,为cpdc_check_embed):

check embed screen in Xcode

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

虽然不是输出口(outlet),但比Matt的(在我看来)更干净,比Caleb的更具体,并且你仍然可以得到一个好看的故事板:

enter image description here


1
你正在使用嵌入式Segue,这是iOS 6的一个功能。我的问题可以追溯到2011年11月。:-p - Constantino Tsarouhas
是的,刚意识到有点泄漏了。试图安装到客户的5.1.1手机上,但不行。 - Ben Mosher
1
现在iOS 6已经发布,这就是正确的答案。 - awolf
1
@awolf,恐怕你是对的。我想“正确答案”也可以演变……我改变了标志,导致马特的声望回到了666(巧合吗?:-p)。 - Constantino Tsarouhas
2
如何从同一容器中获取2个segue?我正在尝试添加第二个嵌入式视图控制器,但是一旦我点击嵌入类型,先前的segue就会消失,只剩下1个segue。 - Ben Quan

15
故事板非常好地处理了内置容器视图控制器,显示到子/根视图控制器的连线,以便关系清晰可见。很好地将子视图控制器和父视图控制器分成不同的场景。如果您想在自己的项目中实现此效果,则有一个技巧,虽然不完美但非常简单明了。假设我有一个行为类似选项卡控制器的容器视图控制器,只有两个选项卡“左”和“右”。我想要一个场景来表示父视图控制器,而另外两个场景则分别代表“左”子视图控制器和“右”子视图控制器。即使这是不可能的,如果我能从容器视图控制器创建到不同场景中它的子对象的IBOutlet,并且当我的容器视图控制器被显示时根据UIViewController documentation中描述的规则建立父/子关系,那就好极了。如果我们有对“左”和“右”子视图控制器的引用,那么我们可以轻松设置这些关系。
这个引用问题的标准解决方案是通过拖拽对象出口到容器视图控制器场景中来创建对子视图控制器的引用,然后将它们的类类型指定为子视图控制器类的实例。 为了像苹果内置容器那样在不同场景中保持子视图分开,我们将使用一个不同的技巧。首先,假设我们在容器类ContainerViewController中声明了以下属性:
@property (nonatomic, strong, readwrite) UIViewController *leftViewController;
@property (nonatomic, strong, readwrite) UIViewController *rightViewController;

在我们的故事板中,选择代表“左”视图控制器的场景。在属性检查器中,将视图控制器的identifier属性设置为"cvc_leftViewController"(“cvc_”指ContainerViewController,但实际上标识符可以是任何你想要的)。对于右侧视图控制器的场景也做同样的操作,将其标识符设置为"cvc_rightViewController"
现在将以下代码插入到ContainerViewControllerviewDidLoad方法中:
if (self.storyboard) {
    _leftViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"cvc_leftViewController"];
    _rightViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"cvc_rightViewController"];
}

当从storyboard中加载ContainerViewController时,它会从各自的场景中获取“left”和“right”视图控制器,并通过其属性设置对它们的引用。现在您可以控制子视图控制器实例,可以随意设置父/子关系。要了解如何正确地执行此操作,请参阅UIViewController文档
这个技巧并不完美,有许多注意事项,但如果您小心使用,可以很好地为您的项目工作。
编辑:虽然这完全是不必要的,也没有任何意义,但如果您真的非常想让storyboard显示从容器到子视图控制器的连接,就像Apple内置的容器一样,只需使用上述方法直接在容器场景和子场景之间设置segue,然后简单地不执行这些segue即可。现在一切都将正常工作,并且看起来也很漂亮。

1
+1听起来是一个不错的技巧,你说得对,它模仿了苹果在storyboard中容器视图控制器的工作方式。与在同一场景中创建子视图控制器相比,主要缺点是您无法在场景之间建立连接。因此,如果您想将子控制器的委托设置为父控制器,则必须在代码中执行此操作--您无法拖动连接。 - Caleb
1
真的。而且,我总是会有点紧张,因为基于标识符字符串建立连接,而不是使用插座所带来的稳定感。 - Matt
酷炫的技巧!我会立刻在我的现有iOS 5应用程序中使用它。我的项目将变得更加简单。苹果已经为iOS 6解决了我的问题(使用Storyboard/IB中的视图控制器容器视图),但这只适用于新应用程序或者当我决定在2-3年内放弃iOS 5时才能使用于现有应用程序。 - Constantino Tsarouhas

7
您是否可以在Storyboard中将子视图控制器与自定义容器视图控制器关联起来?
我认为您的问题是如何连接一个场景中的视图控制器到另一个场景中的视图控制器的输出。我不认为这是可能的,可能是因为Storyboard机制可能没有所有场景同时加载。
您可能会问这个问题,因为您想在从一个场景到另一个场景的过渡中传递一些信息。当您使用Storyboard时,要做的就是在受到segue影响的一个或两个视图控制器中覆盖-prepareForSegue:sender:方法。提供在segue参数中的UIStoryboardSegue对象具有sourceViewController和destinationViewController属性,还有一个identifier属性。您可以使用这些属性来标识即将在视图控制器之间传输数据的segue。
Ray Wenderlich的博客有一个很好的使用storyboards的两部分教程,也许可以帮助您:
  • 第一部分介绍了如何设置故事板项目,添加场景和创建转场。

  • 第二部分介绍了如何使用转场在场景之间切换,包括上面提到的prepareForSeque方法。

iOS 5允许多个视图控制器在同一个场景中活动(虽然应该仍有一个主控制器),因此故事板中的单个场景可能具有多个控制器。您可以使用输出口将这些控制器连接起来,并且可以像在IB中一样配置这些连接:在同一场景中从一个控制器向另一个控制器进行控制拖动。通常的输出口列表将弹出,以便您选择要连接的输出口。


1
不,我的意思是同时使用两个视图控制器。一个“容器”视图控制器包含多个“子”视图控制器。就像分割视图控制器一样,它包含一个主视图控制器和一个详细视图控制器,但是分割视图控制器的子视图控制器(父VC)。 - Constantino Tsarouhas
3
在这种情况下,只需从库中将NSObject拖到您想包含子视图控制器的场景中的Dock中。将对象类型设置为您的视图控制器类。之后,您可以控制拖动以建立与其他对象的连接,包括场景中的其他视图控制器。正如上面所解释的那样,您无法在不同场景中建立对象(视图控制器或其他对象)之间的连接。 - Caleb
所以,与苹果的容器视图控制器不同,如果我想同时显示所有视图控制器(子和父),它们都必须在同一个场景中。好吧,它完成了工作,这就是我想要的。谢谢! - Constantino Tsarouhas
@Randy:看看我的答案,可能更接近你想要的解决方案。 - Matt

5

在一个场景中使用多个控制器的关键(我相信你想要的是这个)是使用IB中对象列表中的神秘对象来表示其他视图控制器并连接其输出口。

这个答案如何在iOS 5中使用storyboard创建自定义视图控制器容器应该会有所帮助。该答案还提供了一个非常有用的实例应用程序。


5
@Ben的回答(除了合理之外)存在一个问题,它仅适用于一级嵌套。在此之后,需要自定义每个随后的VC以在prepareForSegue中保存嵌套视图控制器。
为解决此问题,我花费了过多时间探索基于NSObject的索引,将其添加到Storyboard中,绑定到场景,并根据类型和restorationId在全局索引中注册其父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(...)即可。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接