программная загрузка объекта из подкласса NSView из пера - PullRequest
17 голосов
/ 02 мая 2011

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

Я хочу, чтобы он загружался динамически не с самого начала, поэтому я создал MyView.Xib From MyDocument.m Я сделал:

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

и фон в порядке (вызывает drawrect: он рисуется, как и ожидалось), но все кнопки, которые я положил на xib, не будут отображаться.Я проверил, и они загружены, но их суперпредставление не тот же объект, что и myView.

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

Ответы [ 5 ]

30 голосов
/ 02 мая 2011

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

  • Один или несколько объектов верхнего уровня. Например, экземпляр MyView;
  • Заполнитель владельца файла. Это объект, который существует до загрузки файла пера. Поскольку он существует до загрузки файла пера, он не может совпадать с видом в файле пера.

Сценарий 1: NSNib

Давайте рассмотрим, что в вашем nib-файле есть только один объект верхнего уровня, класс которого MyView, а класс владельца файла MyAppDelegate. В этом случае, учитывая, что файл пера загружается в методе экземпляра 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

Какао предоставляет NSViewController для управления представлениями; как правило, представления загружаются из файла пера. Вы должны создать подкласс NSViewController, содержащий любые необходимые розетки. При редактировании файла пера установите выход view и любой другой выход, который вы определили. Вам также следует установить владельца nib-файла, чтобы его класс был подклассом NSViewController. Тогда:

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

NSViewController автоматически загружает файл пера и устанавливает соответствующие выходы при его отправке -view. Кроме того, вы можете принудительно загрузить файл пера, отправив его -loadView.

5 голосов
/ 08 сентября 2014

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

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;
}
1 голос
/ 16 ноября 2015

Я нашел ответ @Bavarious очень полезным, но с моей стороны все еще требовалось немного белка, чтобы заставить его работать для моего варианта использования - загрузить одно из нескольких определений представления 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
    }
}
0 голосов
/ 17 марта 2016

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

Создать новый xib 'view', используя Xcode.Установите класс владельца файла на 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.

0 голосов
/ 08 января 2016
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)
}

}

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...