自定义 XIB 的“文件所有者”

Posted

技术标签:

【中文标题】自定义 XIB 的“文件所有者”【英文标题】:"File's Owner" of a custom XIB 【发布时间】:2011-08-25 13:50:08 【问题描述】:

如果我创建一个单独的 nib 文件并将其加载到我的视图控制器中并将其分配给 ivar,如下所示:

self.loadingview=[[[NSBundle mainBundle] loadNibNamed:@"loading" owner:self options:nil] objectAtIndex:0];

是否可以为笔尖上的任何东西创建 IBOutlets,比如活动指示器?文件的所有者是否会成为视图控制器,即使 IB 不知道这一点,因为它是在代码中创建的对象?

按照我的方式加载笔尖是可接受的做法吗?

也许更可接受的做法是创建一个 .h/.m,并将其用作 XIB 的类,在这种情况下,File's Owner 是新的自定义类;不过,从自定义类创建 ivar 时,我仍然需要在我的控制器中使用上述方法,对吗?或者有没有更好的方法来完成这一切,而不使用控制器的 xib?

更新:根据一些答案,我做了以下事情。我摆脱了上面的NSBundle 方法,而是这样做:

@property (nonatomic, retain) IBOutlet UIView*loadingview;

在此视图的 nib 文件中,我单击 File's Owner 并将 Custom Class 更改为我的视图控制器。然后我从 File's Owner 控制并拖动到视图,它链接起来很好。

此时我假设我的控制器中有一个 ivar,self.loadingview,其中包含 nib 中的信息。然而事实并非如此。我删除了 NSBundle 方法,假设控制器现在将直接通过 IB 插座加载 nib,但 nib 中的视图没有显示,并且我无法在控制器中控制它。

我在删除 NSBundle 方法时也删除了这个:

[self.view addSubview:self.loadingview];

我不清楚此时视图的实际显示方式;如果是这样,它出现在视图层次结构中的什么位置?也许它落后于其他观点。我也试过这个:

[self.view bringSubviewToFront:self.loadingview];

但视图并不存在。为了找到它,我做了:

//[self.view bringSubviewToFront:self.loadingview];

NSLog(@"index: %i",[[self.view subviews]indexOfObject:self.loadingview]);

有和没有注释部分。我明白了:

index: 2147483647

【问题讨论】:

问题已解决。我了解到,即使从 nib 加载视图,您仍必须使用 addSubview 才能显示它 您的编辑使这个问题完全不同。您应该开始一个新问题,以便以后的搜索者可以找到它。 【参考方案1】:

仅仅因为您创建了一个 nib 并且在该 nib 内部连接了 File's Owner 上的插座并不会自动从您的视图控制器加载该 nib。

您可以为此使用UINib 类:

UINib* nib = [UINib nibWithNibName:@"loading" bundle:[NSBundle mainBundle]];
[nib instantiateWithOwner:self options:nil];

一旦您执行instantiateWithOwner:self,nib 将被加载,self 将用作文件的所有者,并且 nib 中的视图将连接到您的 loadingView 属性。

您仍然需要调用[self.view addSubview:self.loadingView] 将其添加到屏幕。

【讨论】:

不错。你的博客也不错(我过去曾评论过)。你的方法和写[[NSBundle mainBundle] loadNibNamed:@"loading" owner:self options:nil];有什么不同 UINib 缓存 nib 对象,因此效率更高。除此之外,可能没有太大区别。【参考方案2】:

Interface Builder 将知道您通过 owner:self 参数发送的文件所有者。要使您的IBOutlets 出现在 Interface Builder 中,您需要做的是在 Identity and Type 选项卡下在 IB 中设置文件所有者的正确类。

【讨论】:

我已经根据这些答案更新了我的问题,虽然 IBOutlets 出现并在 IB 中工作,但视图本身似乎无法通过我的控制器的 ivar 进行控制。【参考方案3】:

是否可以为笔尖上的任何东西创建 IBOutlets,比如活动指示器?文件的所有者是否会成为视图控制器,即使 IB 不知道这一点,因为它是在代码中创建的对象?

它们不会是 IBOutlets,但是是的,您可以手动进行接线。 IBOutlet 是一个空宏。它专门存在,以便 Interface Builder 可以找到您希望它连接到的属性。但是IBOutlet 关键字对运行时绝对没有影响(编译器甚至看不到它)。类似地,IBAction 是一个被 void 替换的宏。运行时不存在任何关于 Interface Builder 的内容。

当 nib 加载器读取 nib 文件时,它会反序列化每个对象,然后对于每个连接,它会向传递新对象的连接目标发送一个 setter 消息 (setFoo:)。在为 nib 文件中的每个对象完成该操作后,它会将awakeFromNib 发送给它创建的每个对象。连接目标为nil 的对象将发送给“所有者”(在您的示例中为self)。请注意,文件所有者不是由 nib 加载程序创建的。 nib 加载器无法控制文件所有者的实际类。您需要确保文件所有者响应 set...: 调用,否则您将崩溃。

按照我的方式加载笔尖是可接受的做法吗?

我不会依赖loadingView 作为第一个***对象。对象的顺序没有定义。相反,您应该使用 IB 将视图连接到文件所有者的 loadingView 属性。

也许更可接受的做法是创建一个 .h/.m,并将其用作 XIB 的类,在这种情况下,File's Owner 是新的自定义类;不过,从自定义类创建 ivar 时,我仍然需要在我的控制器中使用上述方法,对吗?或者有没有更好的方法来完成这一切,而不使用控制器的 xib?

传统的做法是为 ios 中的每个 nib 文件设置一个UIViewController。但这不是必需的。在 Mac 上,NSViewController 是相当新的,并且不如UIViewController 好用,所以那里没有那么强大的传统。像我所描述的那样手动加载在那里更常见,尽管大多数事情仍然通过NSWindowController 处理。

如果你想做这些事情,需要阅读资源编程指南中的Nib Files。

【讨论】:

我已经根据这些答案更新了我的问题,虽然 IBOutlets 出现并在 IB 中工作,但视图本身似乎无法通过我的控制器的 ivar 进行控制。

以上是关于自定义 XIB 的“文件所有者”的主要内容,如果未能解决你的问题,请参考以下文章

在 xib 中以编程方式自定义 UIView

如何在 Swift 中将内容包装为自定义视图

如何在 Xcode 中创建自定义 UIView?

iphone+相同的xib用于不同的班级

两个 UIViewController 的不同 UITableView 中的一个自定义 UITableViewCell

NSUnknownKeyException 即使定义了出口