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

发布于 2024-11-04 20:12:31 字数 1057 浏览 0 评论 0原文

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

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

MyView *myView = [[MyView alloc] initWithFrame:...];
[NSBundle loadNibNamed:@"MyView" owner:myView];
[someview addSubview:myView];
...

并且背景很好(它调用drawrect:并且按预期绘制) 但我放在 xib 上的所有按钮都不会出现。 我已经检查过,它们已加载,但它们的超级视图与 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中正确连接,这就是为什么它们加载得很好(我可以参考它们)。然而,顶视图没有 IBOutlet。因此,当 NSBundle 加载笔尖时,顶视图会被分配给其他某个对象。我认为如果我将 IB 中的顶部视图设置为来自类:MyView 并将 myView 作为 [NSBundle loadNibNamed:@"MyView] 中的所有者,就会发生这种情况“owner:myView]; 但事实似乎并非如此。 我做错了什么?

How do I correctly load an object that is a subclass of NSView using a Xib?

I want it to be loaded dynamically not from the beginning so I made a MyView.Xib
From MyDocument.m I did:

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.

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?

I wish there was something similar to this for the 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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

滥情空心 2024-11-11 20:12:31

请注意,nib 文件包含:

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

场景 1:NSNib

假设您的 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

看起来 -[NSNib instantiateNibWithOwner:topLevelObjects:] 的第一个参数可以是 < code>nil 因此您不必指定所有者,因为您似乎对拥有一个所有者不感兴趣:

if (! [nib instantiateWithOwner:nil topLevelObjects:&topLevelObjects]) // error

虽然这有效,但我不会依赖它,因为它没有记录。

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

场景2:NSViewController

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

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

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

Note that a nib file contains:

  • 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.

Scenario 1: 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:

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:

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

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:

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.

隐诗 2024-11-11 20:12:31

可以参考这个分类

https://github.com/peterpaulis/NSView-NibLoading- /树/主

+ (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;
}

You can refer to this category

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;
}
柳絮泡泡 2024-11-11 20:12:31

我发现 @Bvarious 的答案非常有用,但我仍然需要一些松鼠才能使其适合我的用例 - 将几个 xib 的视图定义之一加载到现有的“容器”视图中。这是我的工作流程:

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

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

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

4) 连接任何插座和对文件所有者的操作,特别是 .view

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
    }
}

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:

1) In the .xib, create the view in IB as expected.

2) DO NOT add an object for the new view's viewController, but rather

3) Set the .xib's File's Owner to new view's viewController

4) Connect any outlets & actions to File's Owner, and, specifically, .view

5) Call loadInspector (see below).

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
    }
}
冷夜 2024-11-11 20:12:31

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

使用 Xcode 创建一个新的“视图”xib。将文件所有者的类设置为 NSViewController,并将其 view 出口设置为您的目标视图。将目标视图的类设置为您的 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 子类时它也可以工作。

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.

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.

Now, in your NSView subclass, implement the designated initializers:

- (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.

灯下孤影 2024-11-11 20:12:31
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)
}

}

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)
}

}

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文