将子视图控制器链接到情节提要中的父视图控制器

Posted

技术标签:

【中文标题】将子视图控制器链接到情节提要中的父视图控制器【英文标题】:Linking child view controllers to a parent view controller within storyboard 【发布时间】:2012-01-02 04:16:08 【问题描述】:

您能否将子视图控制器与 Storyboard 中的自定义容器视图控制器相关联?

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

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

【问题讨论】:

【参考方案1】:

作为 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):

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

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

【讨论】:

您正在使用嵌入转场,这是 ios 6 的一项功能。我的问题可以追溯到 2011 年 11 月。:-p 是的。刚刚意识到我让猫从袋子里出来了一点。尝试安装到客户的 5.1.1 手机;没有骰子。 现在iOS 6已经发布了,这是正确的答案。 @awolf 恐怕你是对的。我猜“正确答案”也可以进化……我改变了标志,导致马特的代表回到 666(巧合?:-p)。 如何从同一个容器中获得 2 个 segue?我正在尝试添加第二个嵌入式视图控制器,但只要我单击嵌入类型,previos segue 就会消失,我只剩下 1 个 segue。【参考方案2】:

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

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

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

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

然而,为了让孩子在不同的场景(如 Apple 的内置容器)中分开,我们将使用不同的技巧。首先,假设我们在容器类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"];

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

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

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

【讨论】:

+1 听起来是个不错的技术,你说得对,它模仿了 Apple 的容器视图控制器在故事板中的工作方式。与在与容器相同的场景中创建子视图控制器相比,主要缺点是您无法在场景之间建立连接。因此,如果您想将子控制器的委托设置为父控制器,则必须在代码中执行此操作——不能只拖动连接。 是的。此外,我总是对标识符字符串的基础连接感到有点紧张,而不是插座带来的稳定感。 酷黑客!我会像现在一样在我现有的 iOS 5 应用程序中使用它。我的项目会简单得多。 Apple 解决了我的 iOS 6 问题(在 Storyboard/IB 中使用视图控制器容器视图),但这是针对新应用程序或当我决定在 2-3 年内为现有应用程序放弃 iOS 5 时。【参考方案3】:

您能否将子视图控制器关联到自定义容器视图 Storyboard 中的控制器?

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

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

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

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

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

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

【讨论】:

不,我的意思是同时使用两个视图控制器。一个 container 视图控制器,它自身包含多个 child 视图控制器。类似于拆分视图控制器,它包含一个主视图控制器和一个详细视图控制器,但是拆分视图控制器(父 VC)的子级。 在这种情况下,只需将 NSObject 从库中拖到要包含子视图控制器的场景的停靠栏中。将对象的类型设置为您的视图控制器类。之后,您可以控制并拖动以建立与其他对象的连接,包括场景中的其他视图控制器。如上所述,您不能做的是在不同场景中的对象(视图控制器或其他)之间建立连接。 因此,与 Apple 的容器视图控制器不同,如果我想同时显示所有视图控制器(子视图控制器和父视图控制器),它们必须位于同一场景中。好吧,它完成了工作,这就是我想要的。谢谢! @Randy:请参阅我的答案以获得可能更接近您正在寻找的替代解决方案。【参考方案4】:

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

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

【讨论】:

【参考方案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(...)

【讨论】:

以上是关于将子视图控制器链接到情节提要中的父视图控制器的主要内容,如果未能解决你的问题,请参考以下文章

将 XIB 中的按钮链接到情节提要中的视图控制器

将新的视图控制器链接到情节提要?

iOS - 如何将选项卡与情节提要中的特定视图控制器链接?

我在情节提要中的对象没有与代码正确链接

如何将子视图添加到情节提要中的自定义 UITableViewCell

在页面视图控制器中以编程方式使用情节提要中的视图控制器