macos 以编程方式从 nib 的 NSView 子类加载对象
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5855154/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
programmatically loading object from subclass of NSView from nib
提问by nacho4d
How do I correctly load an object that is a subclass of NSView using a Xib?
如何使用 Xib 正确加载作为 NSView 子类的对象?
I want it to be loaded dynamically not from the beginning so I made a MyView.Xib From MyDocument.m I did:
我希望它不是从一开始就动态加载,所以我从 MyDocument.m 创建了一个 MyView.Xib 我做了:
MyView *myView = [[MyView alloc] initWithFrame:...];
[NSBundle loadNibNamed:@"MyView" owner:myView];
[someview addSubview:myView];
...
and the background is fine (it calls drawrect: and it's drawn as expected)
but all the buttons I put on the xib won't appear.
I have checked, and they are loaded BUT their superview is not the same object as myView
.
并且背景很好(它调用 drawrect: 并且按预期绘制)但是我放在 xib 上的所有按钮都不会出现。我已经检查过,并且它们已加载但它们的超级视图与myView
.
Why is this? I think I am missing something in the Xib but I don't know exactly what. In other words: How do I make sure that the root view in my xib is the same object as file's owner?
为什么是这样?我想我在 Xib 中遗漏了一些东西,但我不知道到底是什么。换句话说:如何确保我的 xib 中的根视图与文件所有者是同一个对象?
I wish there was something similar to this for the mac:
我希望mac有类似的东西:
NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:@"MyView" owner:self options:nil]; //in iOS this will load the xib
MyView* myView = [ nibViews objectAtIndex:1];
[someview addSubview:myView];
...
Thanks in advance.
提前致谢。
EDIT
编辑
I think I have realised the origin of the problem...(??)
我想我已经意识到问题的根源了......(??)
In MyView class I have various IBOutlet which are connected correctly in IB that is why they load just fine (I can refer them). However there is no IBOutlet for the top view. So when NSBundle loads the nib, the top view gets assigned to some other object. I thought this will happen if I set my top view in IB to be from class:MyView
and put myView
as the owner in [NSBundle loadNibNamed:@"MyView" owner:myView];
but it seems not to be the case.
What am I doing wrong?
在 MyView 类中,我有各种 IBOutlet,它们在 IB 中正确连接,这就是为什么它们加载得很好(我可以参考它们)。但是,顶视图没有 IBOutlet。所以当 NSBundle 加载笔尖时,顶视图被分配给其他一些对象。我认为如果我在 IB 中将我的顶视图设置为来自 class:MyView
并myView
作为所有者放入,[NSBundle loadNibNamed:@"MyView" owner:myView];
但事实并非如此。我究竟做错了什么?
回答by
Note that a nib file contains:
请注意,nib 文件包含:
- One or more top-level objects. For example, an instance of
MyView
; - A file's owner placeholder. This is an object that exists prior to the nib file being loaded. Since it exists before the nib file is loaded, it cannot be the same as the view in the nib file.
- 一个或多个顶级对象。例如,一个实例
MyView
; - 文件的所有者占位符。这是在加载 nib 文件之前存在的对象。由于在加载nib文件之前就存在,所以不能和nib文件中的视图相同。
Scenario 1: NSNib
场景一: NSNib
Let's consider that your nib file has only one top-level object whose class is MyView
, and the file's owner class is MyAppDelegate
. In that case, considering that the nib file is loaded in an instance method of MyAppDelegate
:
假设您的 nib 文件只有一个顶级对象,其类为MyView
,而文件的所有者类为MyAppDelegate
。在这种情况下,考虑到 nib 文件是在以下实例方法中加载的MyAppDelegate
:
NSNib *nib = [[[NSNib alloc] initWithNibNamed:@"MyView" bundle:nil] autorelease];
NSArray *topLevelObjects;
if (! [nib instantiateWithOwner:self topLevelObjects:&topLevelObjects]) // error
MyView *myView = nil;
for (id topLevelObject in topLevelObjects) {
if ([topLevelObject isKindOfClass:[MyView class]) {
myView = topLevelObject;
break;
}
}
// At this point myView is either nil or points to an instance
// of MyView that is owned by this code
It looks like the first argument of -[NSNib instantiateNibWithOwner:topLevelObjects:]
can be nil
so you wouldn't have to specify an owner since it seems that you aren't interested in having one:
看起来-[NSNib instantiateNibWithOwner:topLevelObjects:]
can的第一个参数是nil
这样您就不必指定所有者,因为您似乎对拥有一个不感兴趣:
if (! [nib instantiateWithOwner:nil topLevelObjects:&topLevelObjects]) // error
Although this works, I wouldn't rely on it since it's not documented.
虽然这有效,但我不会依赖它,因为它没有记录。
Note that you have ownership of the top level objects, hence you are responsible for releasing them when they're no longer needed.
请注意,您拥有顶级对象的所有权,因此您有责任在不再需要它们时释放它们。
Scenario 2: NSViewController
场景2: NSViewController
Cocoa provides NSViewController
to manage views; usually, views loaded from a nib file. You should create a subclass of NSViewController
containing whichever outlets are needed. When editing the nib file, set the view
outlet and whichever other outlets you may have defined. You should also set the nib file's owner so that its class is this subclass of NSViewController
. Then:
Cocoa 提供NSViewController
管理视图;通常,视图从 nib 文件加载。您应该创建一个NSViewController
包含任何需要的出口的子类。编辑 nib 文件时,设置view
插座和您可能定义的任何其他插座。您还应该设置 nib 文件的所有者,使其类是NSViewController
. 然后:
MyViewController *myViewController = [[MyViewController alloc] initWithNibName:@"MyView" bundle:nil];
NSViewController
automatically loads the nib file and sets the corresponding outlets when you send it -view
. Alternatively, you can force it to load the nib file by sending it -loadView
.
NSViewController
发送时自动加载nib文件并设置相应的插座-view
。或者,您可以通过发送-loadView
.
回答by Peter Lapisu
You can refer to this category
你可以参考这个分类
https://github.com/peterpaulis/NSView-NibLoading-/tree/master
https://github.com/peterpaulis/NSView-NibLoading-/tree/master
+ (NSView *)loadWithNibNamed:(NSString *)nibNamed owner:(id)owner class:(Class)loadClass {
NSNib * nib = [[NSNib alloc] initWithNibNamed:nibNamed bundle:nil];
NSArray * objects;
if (![nib instantiateWithOwner:owner topLevelObjects:&objects]) {
NSLog(@"Couldn't load nib named %@", nibNamed);
return nil;
}
for (id object in objects) {
if ([object isKindOfClass:loadClass]) {
return object;
}
}
return nil;
}
回答by Grimxn
I found the answer by @Bavarious very useful, but it still needed some squirrelling on my part to make it work for my use case - load one of several xib's view definitions into an existing "container" view. Here's my workflow:
我发现@Bavious 的答案非常有用,但我仍然需要一些松鼠才能使其适用于我的用例 - 将几个 xib 的视图定义之一加载到现有的“容器”视图中。这是我的工作流程:
1) In the .xib, create the view in IB as expected.
1) 在 .xib 中,按预期在 IB 中创建视图。
2) DO NOT add an object for the new view's viewController, but rather
2) 不要为新视图的 viewController 添加一个对象,而是
3) Set the .xib's File's Owner to new view's viewController
3) 将 .xib 的 File's Owner 设置为新视图的 viewController
4) Connect any outlets & actions to File's Owner, and, specifically, .view
4) 将任何插座和动作连接到文件的所有者,特别是, .view
5) Call loadInspector (see below).
5)调用loadInspector(见下文)。
enum InspectorType {
case None, ItemEditor, HTMLEditor
var vc: NSViewController? {
switch self {
case .ItemEditor:
return ItemEditorViewController(nibName: "ItemEditor", bundle: nil)
case .HTMLEditor:
return HTMLEditorViewController(nibName: "HTMLEditor", bundle: nil)
case .None:
return nil
}
}
}
class InspectorContainerView: NSView {
func loadInspector(inspector: InspectorType) -> NSViewController? {
self.subviews = [] // Get rid of existing subviews.
if let vc = inspector.vc {
vc.loadView()
self.addSubview(vc.view)
return vc
}
Swift.print("VC NOT loaded from \(inspector)")
return nil
}
}
回答by charles
Here is a way to write the NSView subclass so the view itself comes fully from a separate xib and has everything set up correctly. It relies on the fact that init
can change the value of self
.
这是一种编写 NSView 子类的方法,因此视图本身完全来自单独的 xib 并且所有设置都正确。它依赖于init
可以改变 的值的事实self
。
Create a new 'view' xib using Xcode. Set the File Owner's class to NSViewController, and set its view
outlet to your target view. Set the class of the target view to your NSView subclass. Layout your view, connect outlets etc.
使用 Xcode 创建一个新的“视图”xib。将 File Owner 的类设置为 NSViewController,并将其view
出口设置为您的目标视图。将目标视图的类设置为您的 NSView 子类。布局您的视图,连接插座等。
Now, in your NSView subclass, implement the designated initializers:
现在,在您的 NSView 子类中,实现指定的初始值设定项:
- (id)initWithFrame:(NSRect)frameRect
{
NSViewController *viewController = [[NSViewController alloc] init];
[[NSBundle mainBundle] loadNibNamed:@"TheNib" owner:viewController topLevelObjects:NULL];
id viewFromXib = viewController.view;
viewFromXib.frame = frameRect;
self = viewFromXib;
return self;
}
And the same with initWithCoder:
so that it will also work when using your NSView subclass in an another xib.
并且与相同,initWithCoder:
以便在另一个 xib 中使用您的 NSView 子类时它也可以工作。
回答by Chris
class CustomView: NSView {
@IBOutlet weak var view: NSView!
@IBOutlet weak var textField: NSTextField!
required init(coder: NSCoder) {
super.init(coder: coder)!
let frameworkBundle = Bundle(for: classForCoder)
assert(frameworkBundle.loadNibNamed("CustomView", owner: self, topLevelObjects: nil))
addSubview(view)
}
}
}