Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/cocoa/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Objective c 当机场菜单打开时,苹果如何更新机场菜单?(当NSMenu已打开时如何更改它)_Objective C_Cocoa_Statusbar_Nsmenu_Nsmenuitem - Fatal编程技术网

Objective c 当机场菜单打开时,苹果如何更新机场菜单?(当NSMenu已打开时如何更改它)

Objective c 当机场菜单打开时,苹果如何更新机场菜单?(当NSMenu已打开时如何更改它),objective-c,cocoa,statusbar,nsmenu,nsmenuitem,Objective C,Cocoa,Statusbar,Nsmenu,Nsmenuitem,我有一个状态栏项,可以打开一个NSMenu,我有一个委托集,它连接正确(-(void)menuedSupdate:(NSMenu*)menu工作正常)。这就是说,该方法设置为在显示菜单之前调用,我需要监听该方法并触发异步请求,然后在菜单打开时更新菜单,我不知道该怎么做 谢谢:) 编辑 好的,我现在在这里: 单击菜单项(在状态栏中)时,将调用一个选择器来运行NSTask。我使用通知中心来侦听任务何时完成,并编写: [[NSRunLoop currentRunLoop] performSelecto

我有一个状态栏项,可以打开一个NSMenu,我有一个委托集,它连接正确(
-(void)menuedSupdate:(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];
}
这个方法肯定会被调用,因为如果我从菜单中单击并立即返回到它,我会得到一个更新的菜单,其中包含此信息。问题是,菜单打开时,它没有更新。

(如果您想更改菜单的布局,类似于单击“机场”菜单时显示更多信息的方式,请继续阅读。如果您想做一些完全不同的事情,则此答案可能与您希望的不相关。)

关键是。例如,我们将构建一个
NSMenu
,其中包含一个
Do something…
操作。您可以将其编码为:

NSMenu * m = [[NSMenu alloc] init];

NSMenuItem * doSomethingPrompt = [m addItemWithTitle:@"Do something..." action:@selector(doSomethingPrompt:) keyEquivalent:@"d"];
[doSomethingPrompt setTarget:self];
[doSomethingPrompt setKeyEquivalentModifierMask:NSShiftKeyMask];

NSMenuItem * doSomething = [m addItemWithTitle:@"Do something" action:@selector(doSomething:) keyEquivalent:@"d"];
[doSomething setTarget:self];
[doSomething setKeyEquivalentModifierMask:(NSShiftKeyMask | NSAlternateKeyMask)];
[doSomething setAlternate:YES];

//do something with m
现在,你会认为这将创建一个包含两个项目的菜单:“做点什么…”和“做点什么”,你的部分判断是正确的。因为我们将第二个菜单项设置为备用项,并且因为两个菜单项具有相同的等效键(但不同的修改器掩码),所以只会显示第一个菜单项(即默认情况下为
setAlternate:NO
)。然后,当打开菜单时,如果按下表示第二个菜单项的修改器掩码(即,选项键),则菜单项将实时从第一个菜单项转换为第二个菜单项

例如,这就是苹果菜单的工作原理。如果你点击它一次,你会看到一些选项后面有省略号,比如“重启…”和“关机…”。HIG指定如果有省略号,则表示系统将在执行操作之前提示用户进行确认。但是,如果您按下选项键(菜单仍然打开),您会注意到它们会变为“重新启动”和“关闭”。省略号将消失,这意味着如果在按下选项键时选择省略号,则省略号将立即执行,而不会提示用户进行确认


相同的通用功能适用于状态项中的菜单。您可以将展开的信息设置为常规信息的“备用”项,该信息仅在按下选项键时显示。一旦您了解了基本原理,实际上就很容易实现,而不需要太多的技巧。

菜单鼠标跟踪是在特殊的运行循环模式下完成的(
NSEventTrackingRunLoopMode
)。为了修改菜单,您需要发送一条消息,以便在事件跟踪模式下对其进行处理。执行此操作的最简单方法是使用此方法
nsrunlop

[[NSRunLoop currentRunLoop] performSelector:@selector(updateTheMenu:) target:self argument:yourMenu order:0 modes:[NSArray arrayWithObject:NSEventTrackingRunLoopMode]]
您还可以将模式指定为
NSRunLoopCommonModes
,消息将在任何常见运行循环模式期间发送,包括
NSEventTrackingRunLoopMode

然后,您的更新方法将执行以下操作:

- (void)updateTheMenu:(NSMenu*)menu
{
    [menu addItemWithTitle:@"Foobar" action:NULL keyEquivalent:@""];
    [menu update];
}

这里的问题是,即使在菜单跟踪模式下,也需要触发回调

例如,-[NSTask WAITUNTLEXIT]“使用NSDefaultRunLoopMode轮询当前运行循环,直到任务完成”。这意味着它在菜单关闭后才会运行。在这一点上,安排updateTheMenu在NSCommonRunLoopMode上运行并没有任何帮助,毕竟它不能返回时间。我相信NSNotificationCenter观察员也仅在NSDefaultRunLoop模式下触发

如果你能找到某种方法来安排一个回调,即使在菜单跟踪模式下也能运行,那么你就可以设置好了;您可以直接从该回调调用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];
}
运行此命令并按住“文件”菜单,您将看到额外的菜单项每隔半秒出现和消失一次。很明显,“每半秒”不是你想要的,而且Ntimer不明白“我的背景任务何时完成”。但可能有一些同样简单的机制可以使用

如果没有,您可以自己从NSPort子类之一构建它,例如,创建一个NSMessagePort,并在完成时让您的NSTask写入它


唯一一种情况是,如果您试图从运行循环外部调用它,则需要按照上面描述的Rob Keniger的方式显式地安排UpdateMenu。例如,您可以生成一个启动子进程并调用waitpid的线程(该线程将一直阻塞到进程完成),然后该线程将必须调用performSelector:target:argument:order:modes:而不是直接调用updateTheMenu。

非常有用的信息,尽管不是我想要的。不过我肯定会找到它的用处。我真的很喜欢菜单在打开时注入它找到的网络的方式。谢谢这似乎不起作用。我通过NSTask请求数据,等待通知,收到该通知后,填充整个类都可以访问的数据对象,并调用nsrunlop行,该行调用updateTheMenu方法。菜单没有实时更新,但是,我必须在更新的信息显示之前单击它,然后重新打开它。如果使用
nsrunlopcommonmodes
而不是
NSEventTrackingRunLoopMode
,那么它能工作吗?当我这样做时,根本没有调用updateTheMenu,这真的让我很困惑。尽管问了这么长时间,我仍然对这个问题感到好奇,我很高兴看到为什么我所拥有的一切都不起作用