强制可可应用程序主菜单的NSMenu(嵌套子菜单)更新

时间:2010-02-09 21:00:11

标签: cocoa appkit nsmenu

  1. 我有一些子菜单作为主菜单的窗口项子菜单插入
  2. 我有一个我的对象的实例(我们假设它的类名是MenuController)继承自NSObject并且支持来自NSMenuDelegate方法的2个: - numberOfItemsInMenu: - menu:updateItem:atIndex:shouldCancel:
  3. 此实例作为Blue-Object添加到NIB中以便在运行时唤醒
  4. 步骤2-3中的对象配置为子菜单的委托(步骤1)
  5. 现在,我可以在运行时提供子菜单内容。

    接下来,我做了以下操作:我可以添加新项目或从数组中删除旧项目(在包含菜单标题的MenuController内),通过协议和委托映射到真实子菜单。 一切正常。 除了一件事:我喜欢为动态菜单项分配快捷方式。 CMD-1,CMD-2,CMD-3等

    Window / MySubmenu / MyItem1 CMD-1,MyItem2 CMD-2,......

    所以,为了调用一些项目,我不想去Window / MySubmenu / MyItem鼠标点击它,我想只按一个快捷方式,如CMD-3来调用该项目。

    好的,定期按预期工作。但是,一般情况下,除了打开Window / MySubmenu重新加载其内容之外,我无法通知Main Menu我的嵌套子菜单更改。 一种重现问题的稳定方法 - 只需尝试删除一些项目并按下分配给它的旧快捷方式,在创建新项目作为删除替换后 - 宾果 - 快捷方式无法正常工作,然后导航到Window / MySubmenu以查看当前子菜单内容

    我不知道强制主菜单重建其子菜单的方法...... 我试过:[[NSApp mainMenu] update]和NSNotificationCenter的游戏用于发送NSMenuDidAddItemNotification,NSMenuDidRemoveItemNotification,NSMenuDidChangeItemNotification

    我尝试插入我的子菜单并明确调用更新方法 - 没有办法...... 有时候AppKit调用我的委托方法 - 我看到,有时它不想调用任何东西。看起来像一个随机策略。

    如何确保在“部分调用”后,我的子菜单在内部数组修改后将处于实际状态?

3 个答案:

答案 0 :(得分:4)

要实现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];
}

attachedMenu - 是NSMenu *

类型的内部变量

接下来,当您想要随时强制刷新子菜单时 - 只需调用

[self menuNeedsUpdate:nil];

答案 1 :(得分:3)

  

我试过:[[NSApp mainMenu] update] ...

你走在正确的轨道上。这可能是保证Cocoa应用程序中的并行数组的一种情况。

  1. 保留一个可变数组的菜单项,与菜单项所代表的模型对象数组平行。
  2. 收到numberOfItemsInMenu:时,请将您拥有的模型对象数与菜单项数组的数量进行比较。如果更少,请使用the removeObjectsInRange: method缩短菜单项数组。如果更多,请使用NSNull对象填充数组。 (你不能在这里使用nil,因为NSArray只能包含对象,而nil是没有对象。)
  3. 在返回新菜单项之前,当您收到menu:updateItem:atIndex:shouldCancel:replace the object in the array at that index新菜单项时。
  4. 符合NSMenuValidation协议,如the documentation for the update method中所述。在验证方法find the index of the menu item within the array中,然后在模型对象数组中的该索引处获取模型对象,并从中更新菜单项。如果您使用Snow Leopard,可以将菜单项的菜单发送到propertiesToUpdate message,以确定您需要从模型对象中提供哪些属性值。
  5. 需要注意的是,此对象(菜单的委托)也必须是菜单项的目标。我假设它是。如果不是,则在步骤#4失败,因为验证消息被发送到菜单项的目标。

    您可能希望file an enhancement request要求更好的方式。

答案 2 :(得分:0)

numberOfItemsInMenu:来电removeAllItems的代表电话中......这很奇怪,但是菜单不会更新,即使它正在呼叫代表

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

    [menu removeAllItems];

    return [self.templateURLs count] + 2;

}