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

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

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

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

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


person vopilif    schedule 31.08.2015    source источник


Ответы (3)


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

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

Приложение на основе документов

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

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

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

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

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

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

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

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

[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), вы можете выполнить привязку, как написано выше.

Советы: правильно реализуйте UNDO для объектов модели в документе, например. или позорно вызовите 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;
    }
}

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

Я всегда предпочитаю использовать несколько XIB, а не одну гигантскую раскадровку / 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. Свяжите представление, чтобы заменить представление, которое вы хотите заменить  Howto1 Проверьте пример проекта Пример проекта Multi XIB < / а>

Вдохновляйтесь shapeart или lister или TextEdit

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

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

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

person Marek H    schedule 01.09.2015
comment
(CustomDocument *)[[NSDocumentController sharedController] documentForWindow:[[self view] window]]; в этой строке отсутствует инструкция return, а sharedController должно быть sharedDocumentController. - person Can Poyrazoğlu; 13.02.2016
comment
исправлен недостающий возврат - person Marek H; 24.02.2016

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

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

Свойство createdObject в первую очередь связано с привязками какао. В то время как я начинаю знакомиться с привязками, у меня недостаточно информации, чтобы здесь подробно останавливаться. Но поиск в руководстве по программированию привязок может оказаться полезным. В общем случае привязки могут заменить большой объем исходного кода данных, который вам нужно будет написать на ios. Когда это работает, это волшебно. Когда это не работает, это похоже на магию отладки. Иногда бывает сложно увидеть, где что-то пошло не так.

person ac4lt    schedule 31.08.2015

Позвольте мне добавить простой копируемый образец для категории коротких ответов;

В вашем подклассе 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 перезапишите setRepresentObject, чтобы перехватить его значение, отправьте его в super, а затем сделайте вызов, чтобы обновить ваше представление:

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

Merci, bonsoir, все готово.

person Martin-Gilles Lavoie    schedule 19.09.2018