Обновление Force NSMenu (вложенное подменю) для главного меню приложения Cocoa
- У меня есть какое-то подменю, вставленное как подменю "Элемент окна" главного меню
- У меня есть экземпляр моего объекта (предположим, что его имя класса - MenuController), унаследованный от NSObject и поддерживающий 2 из методов NSMenuDelegate: - numberOfItemsInMenu: - menu:updateItem:atIndex:shouldCancel:
- Этот экземпляр добавлен как Blue-Object в NIB для пробуждения во время выполнения
- Объект из шагов 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
Я попытался выйти в подменю и явно вызвать метод update - нет пути... Иногда AppKit вызывает мои методы делегата - и я вижу, что иногда он не хочет ничего вызывать. Похоже на случайную стратегию.
Как я могу убедиться, что после "некоторого вызова" мое подменю будет в текущем состоянии после внутренних изменений массива?
3 ответа
Чтобы реализовать отображение 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];
Я попытался: [[NSApp mainMenu] обновить]...
Вы на правильном пути. Это может быть одна ситуация, в которой параллельный массив в приложении Какао является оправданным.
- Держите изменчивый массив пунктов меню, параллельно вашему массиву объектов модели, которые представляют пункты меню.
- Когда вы получите
numberOfItemsInMenu:
Сравните количество имеющихся у вас объектов модели с количеством элементов массива menu. Если это меньше, используйтеremoveObjectsInRange:
способ сократить массив пунктов меню. Если это больше, дополните массив объектами NSNull. (Вы не можете использоватьnil
здесь, поскольку NSArray может содержать только объекты, иnil
это отсутствие объекта.) - Когда вы получите
menu:updateItem:atIndex:shouldCancel:
замените объект в массиве по этому индексу новым пунктом меню перед возвратом нового пункта меню. - Соответствует протоколу NSMenuValidation, как указано в документации для
update
метод. В вашем методе проверки найдите индекс элемента меню в массиве, затем получите объект модели по этому индексу в массиве объектов модели и обновите элемент меню из него. Если вы находитесь на Snow Leopard, вы можете отправить менюpropertiesToUpdate
сообщение, чтобы определить, какие значения свойств вам нужно присвоить из объекта модели.
Предостережение заключается в том, что этот объект, делегат меню, также должен быть целью пунктов меню. Я предполагаю, что это так. Если это не так, на шаге 4 произойдет сбой, так как сообщения проверки отправляются адресатам пунктов меню.
Вы можете подать запрос на улучшение, прося лучшего способа.
В делегат призыв к numberOfItemsInMenu:
вызов removeAllItems
... это довольно странно, но в остальном меню не обновляется, даже если он вызывает делегата
- (NSInteger)numberOfItemsInMenu:(NSMenu*)menu {
[menu removeAllItems];
return [self.templateURLs count] + 2;
}