Objective c 从nib以编程方式从NSView的子类加载对象

Objective c 从nib以编程方式从NSView的子类加载对象,objective-c,cocoa,macos,Objective C,Cocoa,Macos,如何使用Xib正确加载作为NSView子类的对象 我希望它能够动态加载,而不是从一开始就加载,所以我制作了一个MyView.Xib 从MyDocument.m我做了: MyView *myView = [[MyView alloc] initWithFrame:...]; [NSBundle loadNibNamed:@"MyView" owner:myView]; [someview addSubview:myView]; ... 背景很好(它调用drawrect:并按预期绘制) 但是我放在

如何使用Xib正确加载作为NSView子类的对象

我希望它能够动态加载,而不是从一开始就加载,所以我制作了一个MyView.Xib 从MyDocument.m我做了:

MyView *myView = [[MyView alloc] initWithFrame:...];
[NSBundle loadNibNamed:@"MyView" owner:myView];
[someview addSubview:myView];
...
背景很好(它调用drawrect:并按预期绘制) 但是我放在xib上的所有按钮都不会出现。 我已检查,它们已加载,但它们的superview与
myView
的对象不同

为什么会这样?我想我在Xib中遗漏了一些东西,但我不知道具体是什么。 换句话说:如何确保xib中的根视图与文件所有者是同一个对象

我希望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];
...
提前谢谢

编辑

我想我已经意识到问题的根源…(??)

在MyView类中,我有各种IBOutlet,它们在IB中正确连接,这就是为什么它们加载得很好(我可以参考它们)。但是,顶视图没有IB插座。因此,当NSBundle加载nib时,俯视图将被分配给其他对象。我想,如果我将IB中的顶级视图设置为来自类:
MyView
,并将
MyView
作为
[NSBundle loadNibNamed:@“MyView”所有者:MyView]中的所有者,就会发生这种情况但情况似乎并非如此。

我做错了什么?

请注意,nib文件包含:

  • 一个或多个顶级对象。例如,
    MyView
    的一个实例
  • 文件的所有者占位符。这是加载nib文件之前存在的对象。由于它在加载nib文件之前就存在,因此它不能与nib文件中的视图相同
场景1:
NSNib

让我们考虑一下,您的NIB文件只有一个顶级对象,其类是“代码> MyVIEW/CODE >,并且文件的所有者类是<代码> MyAppDeAltAs/COD>。在这种情况下,考虑到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
它看起来像是
-[NSNib instanceenibwithowner:topLevelObjects:][/code>的第一个参数可以是
nil
,因此您不必指定所有者,因为您似乎对拥有所有者不感兴趣:

if (! [nib instantiateWithOwner:nil topLevelObjects:&topLevelObjects]) // error
虽然这是可行的,但我不会依赖它,因为它没有文档记录

请注意,您拥有顶级对象的所有权,因此您有责任在不再需要它们时释放它们

场景2:
NSViewController
Cocoa提供
NSViewController
来管理视图;通常,从nib文件加载的视图。您应该创建一个子类
NSViewController
,包含所需的任何插座。编辑nib文件时,设置
视图
出口和您可能定义的任何其他出口。您还应该设置nib文件的所有者,使其类是
NSViewController
的子类。然后:

MyViewController *myViewController = [[MyViewController alloc] initWithNibName:@"MyView" bundle:nil];

NSViewController
在发送nib文件时自动加载该文件并设置相应的出口
-view
。或者,您可以通过发送nib文件来强制它加载nib文件
-loadView

您可以参考此类别


我发现@bavaried的答案非常有用,但我仍然需要一些松鼠来让它适用于我的用例——将几个xib视图定义中的一个加载到现有的“容器”视图中。以下是我的工作流程:

1) 在.xib中,按预期在IB中创建视图

2) 不要为新视图的viewController添加对象,而是

3) 将.xib文件的所有者设置为新视图的viewController

4) 将任何出口和操作连接到文件的所有者,特别是,
。查看

5) 致电loadInspector(见下文)


}这里有一种编写NSView子类的方法,这样视图本身就完全来自一个单独的xib,并且所有内容都设置正确。它依赖于
init
可以更改
self
的值这一事实

使用Xcode创建新的“视图”xib。将文件所有者的类设置为NSViewController,并将其
视图
出口设置为目标视图。将目标视图的类设置为NSView子类。布局视图、连接插座等

现在,在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;
}

initWithCoder:
相同,因此当在另一个xib中使用NSView子类时,它也可以工作。

尝试将它们移动到前面,可能它会将其拖到后面,并且无法看到,因为背景我刚刚尝试过。。。这并没有发生;(在您的nib文件中,是否存在类为
MyView
的顶级视图?nib文件中是否存在其他顶级对象?文件所有者的类是什么?在我的nib中:文件的所有者类是
MyView
其中的顶级视图当前为类
NSView
,但我也尝试了
MyView
。两者都不起作用我的nib只有一个顶级对象,其类为“MyView”,但文件所有者为“MyView”还有,这可能吗?。如果在加载nib文件之前已经创建了另一个
MyView
实例,这是可能的。请注意,这意味着该另一个实例要么是通过编程方式创建的,要么是从另一个nib文件加载的,与MyView.nib不同。我有已创建的MyView*MyView实例使用[[MyView alloc]initWithFrame:];我在[NSBundle loadNib:@“MyView”所有者:MyView]中使用它;我仍然不明白这为什么不起作用;(@nacho4d当然可以,但它与从nib文件加载的不一样。它将是一个不同的视图,这就是为什么您要面对问题中描述的问题。正如我在回答中所写,“因为它(文件的所有者)在加载nib文件之前存在,它不能与nib文件中的视图相同。是的,经常使用UIViewController的子类,但有时我将其作为UIV的子类
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
    }
}
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)
}
- (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;
}