【问题标题】:How to load a XIB with a top-level ViewController?如何使用顶级 ViewController 加载 XIB?
【发布时间】:2013-09-30 20:40:29
【问题描述】:

我有一个这样的视图控制器根元素的笔尖:

所以我可以使用自动布局相对于顶部和底部布局指南定位元素。

当我第一次尝试加载这个笔尖时

SearchViewControllerPro* searchViewController = [[SearchViewControllerPro alloc]initWithNibName:@"SearchViewControllerPro" bundle:[NSBundle mainBundle]];

我得到了以下运行时异常:

由于未捕获的异常而终止应用程序 'NSInternalInconsistencyException',原因:'-[UIViewController _loadViewFromNibNamed:bundle:] 加载了“SearchViewControllerPro”笔尖,但未设置视图出口。'

谷歌搜索它向我指出的错误,需要将 xib 的文件所有者设置为我的视图控制器的类,并且必须将视图出口设置为 xib 中的视图对象。如果这样做,则会收到以下运行时错误:

由于未捕获的异常而终止应用程序 'UIViewControllerHierarchyInconsistency',原因:'一个视图只能是 一次最多关联一个视图控制器!视图 > 与 关联。在将此视图与关联之前清除此关联 .'

这并不奇怪,因为视图与文件所有者和 nib 的顶级视图控制器相关联。但是我如何才能告诉运行时它们实际上是同一事物而不是两个独立的实体?

编辑: 当我尝试像这样从笔尖解压 ViewController 时,

NSArray* xibContents = [[NSBundle mainBundle] loadNibNamed:@"SearchViewControllerPro" owner:nil options:nil];
SearchViewControllerPro* mapSearchViewController = [xibContents lastObject];

,它也没有好处:

由于未捕获的异常“NSUnknownKeyException”而终止应用程序, 原因:'[setValue:forUndefinedKey:]: 这个类 不符合键视图的键值编码。

临时解决方案:

我找到了一个解决方案,但它并不漂亮。尽管结构如 IB 所示,但视图控制器并不是 xib 中的最后一个对象。所以我有:

__block SearchViewControllerPro* mapSearchViewController = nil;
[xibContents enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    if ([obj isKindOfClass:[SearchViewControllerPro class]]) {
        mapSearchViewController = obj;
    }
}];

这似乎在没有运行时崩溃的情况下工作。然而,除了干净的代码之外,一切都是如此。

【问题讨论】:

  • 关于您的编辑..您的笔尖有错误。您需要确保类名称是正确的,并且您必须手动断开您所做的任何不正确的连接。您将什么设置为 FilesOwner 类?您与 FilesOwner 建立了哪些联系?
  • 好吧,不管你将 FilesOwner 类设置成什么,只要你意识到它会根据你是否执行 [NSBundle mainBundle] loadNibNamed:... 或 SearchViewControllerPro alloc]initWithNibName: ...因此,如果您更改加载方法,您与 FilesOwner 建立的任何连接都将无效(您将崩溃)

标签: ios uiview uiviewcontroller interface-builder xib


【解决方案1】:

我如何告诉运行时它们实际上是相同的 事物而不是两个独立的实体?

你不能,因为它们不是一回事。您已经创建了两个 SearchViewControllerPro 实例。

您需要分配初始化 SearchViewControllerPro 实例或从 nib 取消归档。

如果您决定在 nib 中创建 ViewController,通常访问它的方式与访问您在 nib 中创建的任何其他项目(视图、按钮、文本字段)的方式相同。

向 FilesOwner 对象添加一个 outlet,在 interface builder 中连接连接,并确保在取消归档 nib 时传递该对象。

例如,如果您希望取消归档 nib 的对象为 FilesOwner:

[[NSBundle mainBundle] loadNibNamed:@"SearchViewControllerPro" owner:self options:nil];

【讨论】:

  • 感谢您的回答。我不确定我是否理解它。我只是试图从笔尖内部获取ViewController,但没有运气(请参阅我上面的编辑,这是你的意思吗?)。关于您的评论:是的,我现在知道,我有两个实例。我的意思是,我想将它们解析为同一个对象,并且不知道如何在 IB 或代码中执行此操作,因为分配初始化失败(请参阅上面的错误)。
  • “将两个实例解析为同一个对象”是什么意思?如果我这样做: ob1 = [SearchViewControllerPro alloc] init]; ob2 = [SearchViewControllerPro 分配] 初始化]; (这就是你所做的)我将如何将它们解析为一个实例?两个对象就是两个对象。
  • 让我重新表述这个问题:我知道如何将 nib 作为视图加载到 UIViewController 中。由于对于 iOS7,我需要相对于 topLayoutGuide 重新定位元素,因此我不得不将视图控制器作为顶级元素放在 nib 中,并且到目前为止我尝试加载它的尝试都失败了。您正确地描述了症状-由于笔尖的结构和我尝试实例化视图控制器的方式,我得到了两个对象。但我不知道设置笔尖和加载调用,所以我最终只有一个。我希望这能更好地解释它。
  • 感谢您更新答案。为整个视图控制器创建一个出口是一个巧妙的技巧。 :-)
  • [[NSBundle mainBundle] loadNibNamed: 的注释 - 它在内部调用 viewDidLoad。因此,如果我们从代码中设置视图控制器的一些属性以在viewDidLoad 方法中使用它们,我们就会遇到问题...
【解决方案2】:

以下方法也有效,并且(至少对我而言)比为 ViewController 创建一个出口更明确:

NSArray* xibContents = [[NSBundle mainBundle] loadNibNamed:@"SearchViewControllerPro" owner:nil options:nil];
SearchViewControllerPro* mapSearchViewController = [xibContents objectAtIndex:0];

【讨论】:

    【解决方案3】:
    if (isIPad)
    {
        [[NSBundle mainBundle] loadNibNamed:@"ABC_iPad" owner:self
    options:nil];
    
    }
    else{
        [[NSBundle mainBundle] loadNibNamed:@"ABC_iPhone" owner:self options:nil];
    
    }
    

    【讨论】:

    • 虽然这段代码可能有助于解决问题,但它并没有解释为什么和/或如何回答问题。提供这种额外的背景将显着提高其长期教育价值。请edit您的答案添加解释,包括适用的限制和假设。
    猜你喜欢
    • 2021-04-11
    • 1970-01-01
    • 2016-08-31
    • 2023-04-06
    • 2019-09-17
    • 1970-01-01
    • 1970-01-01
    • 2017-12-09
    • 2021-08-13
    相关资源
    最近更新 更多