Обновление Force NSMenu (вложенное подменю) для главного меню приложения Cocoa - PullRequest
2 голосов
/ 10 февраля 2010
  1. У меня есть несколько подменю, вставленных как подменю Window в главном меню
  2. У меня есть экземпляр моего объекта (предположим, что его имя класса - MenuController), унаследованного от NSObject и поддерживающего 2 из методов NSMenuDelegate: - numberOfItemsInMenu: - меню: updateItem: atIndex: shouldCancel:
  3. Этот экземпляр добавлен как Blue-Object в NIB для пробуждения во время выполнения
  4. Объект из шагов 2-3 настроен как делегат для подменю (шаг 1)

Теперь я могу предоставить содержимое подменю во время выполнения.

Далее я делаю следующее: я могу добавлять новые элементы или удалять старые из массива (внутри MenuController, который содержит заголовки меню), который отображается в реальном подменю с помощью протокола и делегата. Все работает просто отлично. За исключением одного: я люблю назначать ярлыки для своих динамических пунктов меню. CMD-1, CMD-2, CMD-3 и т. Д.

Окно / MySubmenu / MyItem1 CMD-1, MyItem2 CMD-2, ...

Итак, для вызова некоторых предметов я не хочу идти в Window / MySubmenu / MyItem, чтобы щелкать по нему мышью, я хочу нажать всего одну комбинацию клавиш, например CMD-3, чтобы вызвать предмет.

Хорошо, периодически это работает, как ожидалось. Но, как правило, я не могу сообщить Главному меню о своих вложенных изменениях в подменю, кроме как открыть окно / MySubmenu, чтобы перезагрузить его содержимое. Один стабильный способ воспроизвести проблему - просто попытайтесь удалить какой-либо элемент и нажмите назначенный ему старый ярлык, после того как вы создадите новый элемент вместо замененного удаленного - bingo - ярлык не будет работать, прежде чем вы перейдете в Window / MySubmenu для просмотра текущего содержимого подменю. .

Я не знаю, как заставить главное меню перестроить подменю ... Я пытался: [[NSApp mainMenu] update] и игры с NSNotificationCenter для отправки NSMenuDidAddItemNotification, NSMenuDidRemoveItemNotification, NSMenuDidChangeItemNotification

Я попытался выйти в подменю и явно вызвать метод обновления - нет пути ... Иногда AppKit вызывает мои методы делегата - и я вижу это, иногда он не хочет ничего вызывать. Похоже на случайную стратегию.

Как я могу убедиться, что после "некоторого вызова" мое подменю будет в текущем состоянии после изменений внутреннего массива?

Ответы [ 3 ]

4 голосов
/ 10 февраля 2010

Чтобы реализовать отображение 1: 1, внедрите в делегат эти 3 метода:

- (BOOL)menu:(NSMenu *)menu
updateItem:(NSMenuItem *)item 
atIndex:(NSInteger)index shouldCancel:(BOOL)shouldCancel

и

- (NSInteger)numberOfItemsInMenu:(NSMenu *)menu

и

- (void)menuNeedsUpdate:(NSMenu *)menu
{
    if (!attachedMenu)
        attachedMenu = menu;
    if (!menu)
        menu = attachedMenu;
    NSInteger count = [self numberOfItemsInMenu:menu];
    while ([menu numberOfItems] < count)
        [menu insertItem:[[NSMenuItem new] autorelease] atIndex:0];
    while ([menu numberOfItems] > count)
        [menu removeItemAtIndex:0];
    for (NSInteger index = 0; index < count; index++)
        [self menu:menu updateItem:[menu itemAtIndex:index] atIndex:index shouldCancel:NO];
}

attachMenu - это внутренняя переменная типа NSMenu *

Далее, когда вы хотите принудительно обновить подменю, в любое время - просто позвоните

[self menuNeedsUpdate:nil];
3 голосов
/ 10 февраля 2010

Я пытался: [[NSApp mainMenu] обновить]…

Вы на правильном пути. Это может быть одна ситуация, в которой параллельный массив в приложении Какао является оправданным.

  1. Храните изменчивый массив пунктов меню параллельно вашему массиву объектов модели, которые представляют пункты меню.
  2. Когда вы получите numberOfItemsInMenu:, сравните количество имеющихся у вас объектов модели с количеством элементов массива меню. Если его меньше, используйте метод removeObjectsInRange: , чтобы сократить массив элементов меню. Если это больше, дополните массив объектами NSNull . (Вы не можете использовать nil здесь, поскольку NSArray может содержать только объекты, а nil - это отсутствие объекта.)
  3. Когда вы получаете menu:updateItem:atIndex:shouldCancel:, замените объект в массиве с этим индексом новым пунктом меню перед возвратом нового пункта меню.
  4. Соответствует протоколу NSMenuValidation, как указано в документации по update методу . В вашем методе проверки найдите индекс элемента меню в массиве , затем получите объект модели по этому индексу в массиве объектов модели и обновите элемент меню из него. Если вы работаете в Snow Leopard, вы можете отправить в меню этого пункта меню propertiesToUpdate сообщение , чтобы определить, какие значения свойств вам нужно передать объекту модели.

Предостережение заключается в том, что этот объект, делегат меню, также должен быть целью пунктов меню. Я предполагаю, что это так. Если это не так, на шаге 4 произойдет сбой, так как сообщения проверки отправляются адресатам пунктов меню.

Возможно, вы захотите подать запрос на улучшение с просьбой найти лучший способ.

0 голосов
/ 19 мая 2016

В вызове делегата для numberOfItemsInMenu: вызова removeAllItems ... это довольно странно, но в остальном меню не обновляется, даже если он вызывает делегата

- (NSInteger)numberOfItemsInMenu:(NSMenu*)menu {

    [menu removeAllItems];

    return [self.templateURLs count] + 2;

}
...