我有一个状态栏项目,该项目会弹出以打开NSMenu,并且我有一个委托(delegate)集,并且已正确连接(-(void)menuNeedsUpdate:(NSMenu *)menu
正常工作)。就是说,该方法被设置为在显示菜单之前调用,我需要侦听并触发一个异步请求,然后在打开菜单时更新菜单,我不知道应该怎么做。 。
谢谢 :)
编辑
好的,我现在在这里:
当您单击菜单项(在状态栏中)时,将调用一个运行NSTask的选择器。我使用通知中心来侦听该任务的完成时间,并写道:
[[NSRunLoop currentRunLoop] performSelector:@selector(updateTheMenu:) target:self argument:statusBarMenu order:0 modes:[NSArray arrayWithObject:NSEventTrackingRunLoopMode]];
并具有:
- (void)updateTheMenu:(NSMenu*)menu {
NSMenuItem *mitm = [[NSMenuItem alloc] init];
[mitm setEnabled:NO];
[mitm setTitle:@"Bananas"];
[mitm setIndentationLevel:2];
[menu insertItem:mitm atIndex:2];
[mitm release];
}
该方法之所以被称为“方法”,是因为如果我单击菜单外的并立即返回菜单,则会得到包含此信息的更新菜单。问题是它没有更新-当菜单打开时。
最佳答案
这里的问题是,即使在菜单跟踪模式下,您也需要触发回调。
例如,-[NSTask waitUntilExit]“使用NSDefaultRunLoopMode轮询当前运行循环,直到任务完成为止”。这意味着它只有在菜单关闭后才能运行。那时,安排updateTheMenu运行在NSCommonRunLoopMode上无济于事-毕竟它不能回到过去。我相信NSNotificationCenter观察者也只会在NSDefaultRunLoopMode中触发。
如果您找到了某种安排即使在菜单跟踪模式下也可以运行的回调的方法,那么您已设置好;您可以直接从该回调中调用updateTheMenu。
- (void)updateTheMenu {
static BOOL flip = NO;
NSMenu *filemenu = [[[NSApp mainMenu] itemAtIndex:1] submenu];
if (flip) {
[filemenu removeItemAtIndex:[filemenu numberOfItems] - 1];
} else {
[filemenu addItemWithTitle:@"Now you see me" action:nil keyEquivalent:@""];
}
flip = !flip;
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
NSTimer *timer = [NSTimer timerWithTimeInterval:0.5
target:self
selector:@selector(updateTheMenu)
userInfo:nil
repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}
运行此命令并按住"file"菜单,您将看到多余的菜单项每半秒出现和消失一次。显然,“每半秒”不是您要的内容,并且NSTimer不理解“后台任务完成时”。但是,您可以使用一些同样简单的机制。
如果没有,您可以使用NSPort子类之一自己构建它,例如,创建一个NSMessagePort并在完成时让NSTask对其进行写入。
如果您要尝试从运行循环之外调用它,唯一真正需要的是显式地安排上述Rob Keniger的updateTheMenu计划。例如,您可以生成一个线程,该线程将激发一个子进程并调用waitpid(直到该进程完成才阻塞),然后该线程将不得不调用performSelector:target:argument:order:modes:而不是直接调用updateTheMenu。