Как использовать NSViewController в приложении Cocoa на основе Nsdocument

У меня много опыта работы с iOS, но Cocoa меня немного смущает. Я прочитал несколько документов Apple на Cocoa, но есть еще детали, которые я не мог найти нигде. Похоже, документация была написана до того, как шаблон Xcode на основе NSDocument был обновлен для использования NSViewController, поэтому я не понимаю, как именно я должен организовать свое приложение. Шаблон создает раскадровку с NSWindow, NSViewController.

Я понимаю, что я должен вероятно, подкласс NSWindowController или NSWindow должен иметь ссылку на мой объект модели и установить это в makeWindowControllers(). Но если бы я хотел использовать NSViewController вместо того, чтобы просто помещать все в окно, мне также нужно было бы как-то получить доступ к моей модели. Я замечаю, что в моем контроллере представления есть что-то, называемое representedObject, которое, похоже, предназначено для хранения некоторого объекта модели (чтобы затем быть приведенным), но это всегда ноль. Как это сделать набор?

Мне трудно правильно сформулировать этот вопрос, но я думаю, что я спрашиваю:Как правильно использовать NSViewController в моем приложении на основе документов?

PS: Я понимаю, что NSWindowController обычно предназначен для управления несколькими окнами, которые действуют на одном документе, поэтому, предположительно, если мне нужно только одно окно, мне не нужен NSWindowController. Однако требования могут измениться, и использование NSWindowController может быть лучше в долгосрочной перспективе, правильно?

3 ответов


Я не нырял в раскадровки, но вот как это работает:

если ваше приложение должно поддерживать 10.9 и ниже, создайте пользовательский подкласс NSWindowController

Document based app

поместите такой код в подкласс NSDocument

- (void)makeWindowControllers
{
  CustomWindowController *controller = [[CustomWindowController alloc] init];
  [self addWindowController:controller];
}

если ваше приложение имеет несколько окон, чем добавить их здесь или где-то еще (загружается по требованию), но не забудьте добавить его в массив документа windowscontroller (addWindowController:)

если вы создаете их, но не хотите показывать все окна, то переопределите

- (void)showWindows
{
  [controller showWindow:nil]
}

вы можете в любое время получить доступ к модели в вашем контроллере окна

- (CustomDocument *)document
{
  return [self document];
}

используйте привязки в контроллере окна (подкласс windowcontroller + документ в keypath, который является свойством контроллера окна)

[self.textView bind:@"editable"
                  toObject:self withKeyPath:@"document.readOnly"
                   options:@{NSValueTransformerNameBindingOption : NSNegateBooleanTransformerName}];

в отличие от iOS большинство представлений находятся на экране, поэтому вам нужно полагаться на шаблоны: делегирование, Уведомление, события (цепочка ответчика) и, конечно же, MVC.

10.10 Йосемити Изменения:

NSViewController начиная с 10.10 автоматически добавляется в цепочку ответчика (обычно цель действия неизвестна | nsapp sendAction:to:from:) и все делегаты, такие как viewDidLoad... знакомые из iOS наконец-то реализованы. Это означает, что я больше не вижу большого преимущества подкласса NSWindowCotroller.

подкласс NSDocument является обязательным, и достаточно NSViewController.

вы можете в любое время получить доступ к данным в контроллере вида

- (CustomDocument *)document
{
  return (CustomDocument *)[[NSDocumentController sharedDocumentController] documentForWindow:[[self view] window]];
  //doesn't work if you do template approach
  //NSWindowController *controller = [[[self view] window] windowController];
  //CustomDocument *document = [controller document];
}

Если вам это нравится (в соответствии с KVC/KVO), вы можете сделать привязку, как написано выше.

советы: Правильно реализовать отмену для объектов модели в документе, например, или позорно вызвать updateChangeCount:

[[self.undoManager prepareWithInvocationTarget:self] deleteRowsAtIndexes:insertedIndexes];

не помещайте код, связанный с представлениями / окнами Документ

разделите приложение на несколько NSViewControllers, например

- (void)prepareForSegue:(NSStoryboardSegue *)segue sender:(id)sender {
    if ([segue.identifier isEqualToString:AAPLListWindowControllerShowAddItemViewControllerSegueIdentifier]) {
        AAPLListViewController *listViewController = (AAPLListViewController *)self.window.contentViewController;

        AAPLAddItemViewController *addItemViewController = segue.destinationController;

        addItemViewController.delegate = listViewController;
    }
}

предыдущий код вызывается на windowcontroller с viewcontroller в качестве делегата (снова возможно только после 10.10)

Я всегда предпочитаю использовать несколько XIBs, а не одну гигантскую раскадровку/XIB. Используйте следующий подкласс NSViewController и всегда наследуйте от него:

#import <Cocoa/Cocoa.h>

@interface MyViewController : NSViewController

@property(strong) IBOutlet NSView *viewToSubstitute;

@end

#import "MyViewController.h"

@interface MyViewController ()

@end

@implementation MyViewController

- (void)awakeFromNib
{
  NSView *view = [self viewToSubstitute];
  if (view) {
    [self setViewToSubstitute:nil];
    [[self view] setFrame:[view frame]];
    [[self view] setAutoresizingMask:[view autoresizingMask]];
    [[view superview] replaceSubview:view with:[self view]];

  }
}

@end
  1. добавьте подкласс MyViewController в проект с XIB. Переименовать XIB
  2. добавьте объект NSViewController в XIB и измените его имя подкласса Howto2
  3. измените имя загрузки XIB на имя с шага 1 Howto3
  4. Link view для замены представления, которое вы хотите заменить Howto1 Проверьте пример проекта пример проекта Multi XIB

вдохновить себя shapeart или Листер или TextEdit

и реальное руководство использовать бункер и посмотреть, как другие приложения сделать.

PS: вы можете добавить свои представления / viewcontroller в ответчик цепи вручную.

PS2: если вы новичок не слишком архитектор. Будьте довольны тем, что ваше приложение работает.


Я относительно новичок в этом сам, но, надеюсь, я могу добавить немного понимания.

вы можете использовать контроллеры представления так же, как и в ios. Вы можете установить розетки, цели и тому подобное. Для приложений на основе NSDocument вы можете использовать контроллер представления или контроллер окна, но я думаю, что для большинства приложений вы будете использовать оба с большей частью логики в контроллере представления. Поставьте логику там, где она имеет наибольший смысл. Например, если ваш nsdocument может иметь несколько типы окон затем используют контроллер вида для логики, специфичной для каждого типа, и контроллер окна для логики, применимой ко всем типам.

свойство representedObject в основном связано с привязками Cocoa. Хотя я начинаю знакомиться с привязками, у меня недостаточно фона, чтобы вдаваться в подробности здесь. Но поиск в руководстве по программированию Привязок может быть полезным. В общем случае привязки могут занять место большого количества кода источника данных, который вам понадобится пишите на iOS. Когда это работает, это волшебно. Когда это не работает, это как отладка магии. Это может быть вызов, чтобы увидеть, где все пошло не так.


позвольте мне добавить простую копию-pastable образца на краткосрочную категорию ответа;

в вашем подклассе NSDocument отправьте self к представленному объекту вашего контроллера вида, когда вы вызываетесь для makeWindowControllers:

- (void) makeWindowControllers
{ 
    NSStoryboard*                   storyboard          =   [NSStoryboard storyboardWithName: @"My Story Board" bundle: nil];
    NSWindowController*             windowController    =   [storyboard instantiateControllerWithIdentifier: @"My Document Window Controller"];
MyViewController*    myController               =   (id) windowController.contentViewController;
    [self addWindowController: windowController];
    myController.representedObject = self;
}

в подклассе MyViewController nsviewcontroller перезапишите setRepresentedObject, чтобы поймать его значение, отправьте его в super, а затем сделайте вызов, чтобы обновить свое представление:

- (void) setRepresentedObject: (id) representedObject
{
    super.representedObject = representedObject;
    [self myUpdateWindowUIFromContent];
}