3

У Finder и Notes есть своеобразное поведение, которое я пытаюсь воспроизвести. «Гибкое пространство» в NSToolbar, по-видимому, учитывает размеры разделенного представления. Например, первая группа кнопок выравнивается с левой стороны с правой стороны боковой панели. Вторая группа значков выравнивается с правой стороны первого столбца. Когда я расширяю боковую панель, элементы панели инструментов перемещаются вместе с ней.Выровнять NSToolbarItems с колонками NSSplitView

Можно ли воспроизвести это?

enter image description here


Решение

С решением, предоставленной @KenThomases, я реализовал это следующим образом:

final class MainWindowController: NSWindowController { 
    override func windowDidLoad() { 
     super.windowDidLoad() 
     window?.toolbar?.delegate = self 
     // Make sure that tracking is enabled when the toolbar is completed 
     DispatchQueue.main.async { 
      self.trackSplitViewForFirstFlexibleToolbarItem() 
     } 
    } 
} 

extension MainWindowController: NSToolbarDelegate { 
    func toolbarWillAddItem(_ notification: Notification) { 
     // Make sure that tracking is evaluated only after the item was added 
     DispatchQueue.main.async { 
      self.trackSplitViewForFirstFlexibleToolbarItem() 
     } 
    } 

    func toolbarDidRemoveItem(_ notification: Notification) { 
     trackSplitViewForFirstFlexibleToolbarItem() 
    } 

    /// - Warning: This is a private Apple method and may break in the future. 
    func toolbarDidReorderItem(_ notification: Notification) { 
     trackSplitViewForFirstFlexibleToolbarItem() 
    } 

    /// - Warning: This method uses private Apple methods that may break in the future. 
    fileprivate func trackSplitViewForFirstFlexibleToolbarItem() { 
     guard var toolbarItems = self.window?.toolbar?.items, let splitView = (contentViewController as? NSSplitViewController)?.splitView else { 
      return 
     } 

     // Add tracking to the first flexible space and remove it from the group 
     if let firstFlexibleToolbarItem = toolbarItems.first, firstFlexibleToolbarItem.itemIdentifier == NSToolbarFlexibleSpaceItemIdentifier { 
      _ = firstFlexibleToolbarItem.perform(Selector(("setTrackedSplitView:")), with: splitView) 
      toolbarItems.removeFirst() 
     } 

     // Remove tracking from other flexible spaces 
     for flexibleToolbarItem in toolbarItems.filter({ $0.itemIdentifier == NSToolbarFlexibleSpaceItemIdentifier }) { 
      _ = flexibleToolbarItem.perform(Selector(("setTrackedSplitView:")), with: nil) 
     } 
    } 
} 

ответ

2

Вы можете сделать это с Apple, частными методами, хотя это запрещено в App Store.

Существует частный метод, -setTrackedSplitView:, на NSToolbarItem. В качестве параметра он принимает значение NSSplitView*. Вам нужно вызвать его на элементе панели гибкого пространства, который вы хотите отслеживать разделенным представлением, и передать ему вид разделения, который он должен отслеживать. Чтобы защитить себя от Apple, удалив этот метод, вы должны проверить, отвечает ли метод NSToolbarItem методу, прежде чем пытаться его использовать.

Поскольку пользователь может настроить и перенастроить панель инструментов, вам, как правило, необходимо перечислить элементы панели окна. Для первого, чей идентификатор равен NSToolbarFlexibleSpaceItemIdentifier, вы устанавливаете разделенный вид, который он должен отслеживать. Для всех других элементов гибкого пространства вы очищаете (устанавливаете на nil) разделенный вид для отслеживания. Вам нужно сделать это, когда окно сначала настроено и снова в инструментах делегата -toolbarWillAddItem: и -toolbarDidRemoveItem:. Существует еще один недокументированный метод делегирования, -toolbarDidReorderItem:, где я нашел полезным обновить панель инструментов.

+0

Фантастический, это похоже на работу. Два комментария: (1) 'toolbarWillAddItem:' требует немного другого поведения, потому что элементы панели инструментов не обновляются при вызове этого метода. Вместо этого 'setTrackedSplitView:' должен быть вызван непосредственно в этом сетевом элементе. (2) Установка остальных элементов в нуль приводит к неустойчивому поведению и ошибке типа «непризнанный селектор». – Eitot

+1

Вы должны установить только оставшиеся ** предметы с гибким пространством ** для отслеживания 'nil'. Я предполагаю, что под капотом есть определенный подкласс для этих предметов. Вам нужно очистить отслеживаемый вид разделения для них, потому что вы, возможно, предварительно установили его для одного из них, а затем изменили порядок. Что касается '-toolbarWillAddItem:', мой код откладывает обновление элементов до конца цикла цикла запуска с помощью 'dispatch_async()' в основной очереди. –

+0

Вызов его в 'DispatchQueue.main.async()' работает в моем коде только тогда, когда приложение запущено, но при запуске оно, похоже, не работает. Можете ли вы привести пример того, как вы это сделали? – Eitot