Force NSMenu (nested submenu) update for Main Menu of Cocoa App

跟風遠走 提交于 2019-11-30 07:43:54

To implement 1:1 mapping, implement in delegate these 3 methods:

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

and

- (NSInteger)numberOfItemsInMenu:(NSMenu *)menu

and

- (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 - is internal var of type NSMenu*

Next, when you wanna force refresh the submenu, anytime - just call

[self menuNeedsUpdate:nil];

I tried: [[NSApp mainMenu] update] …

You're on the right track. This may be the one situation in which a parallel array in a Cocoa app is warranted.

  1. Keep a mutable array of menu items, parallel to your array of model objects that the menu items represent.
  2. When you receive numberOfItemsInMenu:, compare the number of model objects you have to the count of the menu-items array. If it's fewer, use the removeObjectsInRange: method to shorten the menu-items array. If it's more, pad the array out with NSNull objects. (You can't use nil here, since an NSArray can only contain objects, and nil is the absence of an object.)
  3. When you receive menu:updateItem:atIndex:shouldCancel:, replace the object in the array at that index with the new menu item before returning the new menu item.
  4. Conform to the NSMenuValidation protocol, as mentioned in the documentation for the update method. In your validation method, find the index of the menu item within the array, then get the model object at that index in the model objects array and update the menu item from it. If you're on Snow Leopard, you can send the menu item's menu a propertiesToUpdate message to determine what property values you need to confer from the model object.

The caveat is that this object, the delegate of the menu, must also be the target of the menu items. I'm assuming that it is. If it isn't, this will fail at step #4, as the validation messages are sent to the menu items' targets.

You may want to file an enhancement request asking for a better way.

In the delegate call for numberOfItemsInMenu: call removeAllItems... it's quite odd, but else the menu doesn't update, even it's calling for a delegate

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

    [menu removeAllItems];

    return [self.templateURLs count] + 2;

}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!