Подтвердить что ты не робот

Программно загружать объект из подкласса NSView из nib

Как правильно загрузить объект, являющийся подклассом NSView, с помощью Xib?

Я хочу, чтобы он загружался динамически не с самого начала, поэтому я создал 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];, но, похоже, это не так. Что я делаю неправильно?

4b9b3361

Ответ 1

Обратите внимание, что файл nib содержит:

  • Один или несколько объектов верхнего уровня. Например, экземпляр MyView;
  • Заполнитель владельца файлов. Это объект, который существует до загрузки загружаемого файла. Поскольку он существует до загрузки файла 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:] может быть 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 автоматически загружает файл nib и устанавливает соответствующие выходы при его отправке -view. Кроме того, вы можете заставить его загрузить файл nib, отправив его -loadView.

Ответ 2

Вы можете обратиться к этой категории

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

Ответ 3

Я нашел ответ на @Bavarious очень полезным, но мне все еще нужно было немного скреститься с моей стороны, чтобы он работал для моего использования. Загрузите одно из нескольких определений представления xib в существующее представление "контейнер". Здесь мой рабочий процесс:

1) В .xib создайте представление в IB, как ожидалось.

2) НЕ добавляйте объект для нового view 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
    }
}

Ответ 4

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

}

Ответ 5

Вот способ записи подкласса NSView, поэтому сам вид полностью исходит из отдельного xib и имеет все правильное настроение. Он полагается на то, что init может изменить значение self.

Создайте новый "вид" xib с помощью Xcode. Установите класс File Owner в 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:, чтобы он также работал при использовании вашего подкласса NSView в другом xib.