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