【发布时间】:2012-01-02 04:16:08
【问题描述】:
您能否将子视图控制器与 Storyboard 中的自定义容器视图控制器相关联?
我可以将子视图控制器链接到选项卡视图控制器,并且可以将一个视图控制器链接到导航控制器。
我必须对容器 VC 做什么才能接受子 VC?
【问题讨论】:
标签: ios xcode cocoa-touch uiviewcontroller uistoryboard
您能否将子视图控制器与 Storyboard 中的自定义容器视图控制器相关联?
我可以将子视图控制器链接到选项卡视图控制器,并且可以将一个视图控制器链接到导航控制器。
我必须对容器 VC 做什么才能接受子 VC?
【问题讨论】:
标签: ios xcode cocoa-touch uiviewcontroller uistoryboard
作为 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 的更具体,而且你仍然会得到一个漂亮的故事板:
【讨论】:
故事板很好地处理了内置容器视图控制器,向子/根视图控制器显示 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"。
现在将以下代码插入ContainerViewController的viewDidLoad方法中:
if (self.storyboard) {
_leftViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"cvc_leftViewController"];
_rightViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"cvc_rightViewController"];
}
当ContainerViewController 从情节提要加载时,它将从各自的场景中获取“左”和“右”视图控制器,并通过其属性设置对它们的引用。现在您已经控制了子视图控制器实例,您可以随意设置父/子关系。要了解如何正确执行此操作,请参阅UIViewController documentation。
这个技巧并不完美,并且有很多注意事项,但如果你小心,你可以让它在你的项目中很好地工作。
编辑:虽然这完全没有必要并且没有任何意义,但如果您真的真的希望故事板显示从您的容器到您的子视图控制器的连接,就像 Apple 的内置容器一样,只需使用我上面的方法,然后在容器场景到子场景之间直接设置segues,并且根本不执行那些segues。现在一切都会正常运行,并且看起来也很漂亮。
【讨论】:
您能否将子视图控制器关联到自定义容器视图 Storyboard 中的控制器?
我认为您在这里要问的是如何将一个场景中的视图控制器连接到不同场景中的视图控制器的插座。我不相信这是可能的,也许是因为情节提要机器可能不会同时加载情节提要中的所有场景。
您可能会问这个问题,因为您想在从一个场景切换到下一个场景时将一些信息从一个视图控制器传递到另一个视图控制器。使用情节提要时执行此操作的方法是在受 segue 影响的一个或两个视图控制器中覆盖 -prepareForSegue:sender:。 segue 参数中提供的 UIStoryboardSegue 对象具有 sourceViewController 和 destinationViewController 属性,以及 identifier 属性。您可以使用这些属性来识别即将在视图控制器之间传输数据的 segue。
Ray Wenderlich 的博客有一个很好的关于使用故事板的两部分教程,可能会对您有所帮助:
iOS 5 允许多个视图控制器在同一场景中处于活动状态(尽管仍应由一个控制器负责),因此情节提要中的单个场景可能有多个控制器。您可以使用插座将这些控制器相互连接,并且您可以像在 IB 中一样配置这些连接:在同一场景中从一个控制器拖动到另一个控制器。通常的插座列表将弹出,让您选择要连接的插座。
【讨论】:
在一个场景中使用多个控制器的关键(我相信你在这里追求的)是使用 IB 中对象列表中的神秘对象来表示另一个视图控制器并连接它的出口。
这个答案How to create custom view controller container using storyboard in iOS 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(...)。
【讨论】: