NSToolbar в Xcode 7, используя раскадровки (NSWindowController -> NSSplitViewController)
Привет, я уже видел этот вопрос несколько раз, но без определенного ответа, поэтому я создал его для xcode 7 и swift2 (которые в любом случае могли немного измениться).
Я создал проект с использованием плат Xcode 7 и Cocoa OSX Story + swift2, поэтому мой проект начался с NSWindowController, который подключается к NSViewController (как и ожидалось!). Я добавил NSToolbar в мой оконный контроллер и добавил NSButton на панель инструментов. Я изменил свой NSViewController, чтобы он стал одним из новых NSSplitViewController, который связывается с тремя NSViewController и отображает их виды горизонтально - с вертикальными разделителями - (аналогично макету, который вы видите в приложении для фотографий или страницах в Yosemite +). Моя конечная цель будет состоять в том, чтобы кнопка на Моей панели инструментов показывала и скрывала первое разделение.
Насколько я понимаю, и я ожидаю, что для достижения этой цели я должен создать действие в NSSplitViewController, которое более или менее изменяет ограничения автоматического макета, как они работают здесь: как свернуть и развернуть представление в Mac приложение?,
И затем каким-то образом связать это действие с NSButton, который находится на панели инструментов... который находится в NSWindowController (далеко вверх и изолирован в иерархии)...
Я уже рассмотрел другие вопросы о NSToolbar и раскадровках и не смог достичь своей цели:
- Видео на YouTube: Cocoa Programming L17 - NSToolbar, который, как мне кажется, наиболее близок к решению проблемы, но его метод не работает для раскадровок, а только создает собственный файл xib.
- В этом вопросе: Как использовать NSToolBar в Xcode 6 и раскадровке? Один человек предлагает создать ссылку, используя первый ответ и ожидая, что все будет подключено во время выполнения (что выглядит немного хитроумно, а не то, как Apple это реализовала бы, я думаю...). Второй человек предложил создать переменную контроллера представления в NSWindowController и манипулировать его свойствами оттуда... но опять же, немного хитроумно.
- Один последний комментарий, который я увидел в этом вопросе и который, кажется, лучший способ решения проблемы (но все же не так хорошо, как я предполагаю, что это может быть), - это добавить NSObjectController в док каждой сцены, и когда сцена загружается, установите значения объектов для контроллера другой сцены. Это действительно лучший способ пойти дальше? Если так, как я мог достигнуть этого?
Apple снова упомянула в WWDC15, что они создали раскадровки для osx и контроллер split-view, который владеет контроллерами представления, чтобы вы могли перенести свою логику и работу на конкретный контроллер представления, так что я ожидал бы сделать все от внутри моего контроллера разделенного вида, так как это цель, которая должна измениться.
Кто-нибудь знает, как этого добиться от самого контроллера вида? Я действительно не смог найти способ подключить мой ToolBarItem к нему.
4 ответа
Хорошо, я создал этот вопрос несколько дней назад, и пока ответа нет, поэтому я отвечаю тем, что недавно сделал, чтобы преодолеть проблему.
После того, как я создал свой проект Xcode, я сделал это:
- Создан подкласс MySplitViewController для NSSplitViewController
Добавлен IBOutlet для каждого NSSplitViewItem. Например:
@IBOutlet слабый var mySplitViewItem: NSSplitViewItem!
Создан подкласс WindowController для NSWindowController
- Добавлен IBAction в классе WindowController, который ссылается на NSToolbarItem (моя кнопка)
Добавлено свойство, которое получает содержимое контроллера окна как MySplitViewController
var mySplitViewController: MySplitViewController {вернуть self.window?.contentViewController как! MySplitViewController }
Теперь я могу получить доступ к свойству контроллера разделенного вида из Window Controller в действии, которое я создал:
mySplitViewController. mySplitViewItem.collapsed = true
Я создал некоторый пример кода, который делает это (но с использованием контроллера представления и изменением текста для метки здесь, на тот случай, если кто-то захочет увидеть работающий проект с таким поведением. И сообщение в блоге об этом тоже:)
Один человек предлагает создать ссылку, используя первый ответ и ожидая, что все будет подключено во время выполнения (что выглядит немного хитроумно, а не то, как Apple это реализовала бы, я думаю...).
Я думаю, что этот метод первого респондента на самом деле правильный путь.
В качестве примера:
Добавьте что-то похожее на следующее, в каком бы виде контроллер не имел смысла.
@IBAction func doSomething(_ sender: AnyObject?) {
print("Do something.")
}
Это волшебным образом проявится в первом респонденте:
В раскадровке щелкните правой кнопкой мыши оранжевый значок "первый ответчик" над контроллером окна, и вы должны увидеть doSomething
в очень длинном списке. Вам просто нужно подключить это к кнопке на панели инструментов.
На следующем снимке экрана вы видите, что моя кнопка "Переключить боковую панель" подключена к toggleSidebar
действие в моем первом ответчике.
Мне даже не пришлось писать этот метод - он предоставляется NSSplitViewController
:
@IBAction open func toggleSidebar(_ sender: Any?)
Итак, я работал над той же проблемой и не нашел решения, которое вы испытали. Я прочитал ваш пост и пытался понять, как я смогу реализовать ваше решение, когда мне пришло в голову использовать уведомление. Примерно через 30 секунд у меня было прекрасно работающее решение:
В вашем windowController добавьте IBAction, чтобы опубликовать уведомление примерно так
-(IBAction)toggleMasterViewClicked:(id)sender
{
[[NSNotificationCenter defaultCenter] postNotificationName:@"TestNotification" object:nil];
}
Подключите это действие к вашему NSToolbarItem
затем в viewController добавьте себя в качестве наблюдателя для этого уведомления, как
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(toggleMasterView:) name:@"TestNotification" object:nil];
В вашем случае селектор будет updateMyLabelText
Я не вижу здесь никаких недостатков. Нет необходимости ссылаться на другие объекты, нет зависимостей. У меня работает без нареканий
Хотя подключение IBActions работает либо с использованием первого ответчика, либо путем добавления «объекта» в сцену, а затем изменения его класса на класс контроллера представления окна, это не помогает с IBOutlets и делегатами, на которые вы хотите указать просмотр контроллера.
Вот как это сделать:
Добавьте панель инструментов к контроллеру представления, а не к его окну. Таким образом, вы можете легко установить все соединения IBOutlet в сцене контроллера представления. Я делал это годами и не обнаружил никаких проблем, даже при использовании вкладок.
Тогда вам нужно будет назначить панель инструментов окна в коде. Например, вот так:
@interface ViewController ()
@property (weak) IBOutlet NSToolbar *toolbar; // connect this in your storyboard to the Toolbar that you moved to the View Controller Scene
@end
- (void)viewWillAppear {
[super viewWillAppear];
self.view.window.toolbar = self.toolbar;
}