如何即使应用程序退出也在菜单栏中显示应用程序图标

3

我的应用程序在菜单栏中显示一个图标,但当应用程序退出时,该图标会从菜单栏中消失。

我们是否有一种编码方式,使应用程序即使退出也始终保留在菜单栏中。

谢谢。


可能是重复的问题:如何为Mac创建菜单栏应用程序 - Mike D
我已经实现了一个菜单栏图标,但是一旦我退出我的应用程序,它就会从菜单栏中消失。我想知道如何使其始终可用,即使我退出应用程序。 - Kishore
你不能这样做,而且这也没有太多意义。如果应用已经退出了,图标会有什么作用呢?如果用户退出了你的菜单栏应用,他们希望图标消失。这就是为什么他们退出它的原因。如果图标不应该消失,那就不要退出应用程序(但是如果你阻止用户这样做,那么请预计他们会感到生气)。 - Ken Thomases
谢谢Ken。但是我想制作一个类似于防病毒软件图标的东西,即使应用程序不在Dock中,它也始终保留在菜单栏中。 - Kishore
2
如果您不希望应用程序的图标出现在 Dock 中,通常会将其设置为 UI 元素(在其 Info.plist 文件中设置 LSUIElement)。如果您希望它有时有 Dock 图标,有时没有,则建议将其拆分为单独的、相互操作的部分:一个位于菜单栏中的 UI 元素和一个显示在 Dock 中的单独的普通应用程序。在 OS X 10.9 及更高版本中,您可以使用 -[NSApplication setActivationPolicy:] 在 UI 元素和常规应用程序之间切换。(方法文档说您不能这样做,但发布说明记录了这个新功能。) - Ken Thomases
1个回答

7
你可以将应用程序切换到后台(附件)模式,然后再切回来。从语义上讲,应用程序从未退出。
基本思路是使用NSApplicationDelegate协议在附件和常规应用程序模式之间切换。已经有了取消退出的方法、捕获所有关闭窗口的方法以及处理用户尝试启动你的应用程序,即使它仍在运行的方法。所以把它们放在一起,就得到下面的代码。
我在这里留下了代码,展示如何加载和卸载由NSWindowController self.wincon控制的主GUI,其中self是应用程序委托对象。它加载并控制一个单独的MainWindow.xib。如果除了主菜单之外没有其他窗口,那么可能是不必要的。
我还有一个需要设置的用户偏好,以启用所有这些行为。默认情况下,它会真正地退出。
在MainMenu.xib中我什么也没有,只有菜单——切换到附件模式将意味着菜单不会显示。
// Helper to close main window and switch to accessory mode
- (void) switchToBackgroundMode
{
    @autoreleasepool {
        // Need to check loaded to prevent closing a closed window and
        //  triggering a second call to applicationShouldTerminateAfterLastWindowClosed
        if ([self.wincon isWindowLoaded]) [self.wincon close];
        self.wincon = nil;
    }

    // Hide the menu and dock icon
    [NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory];
}

#pragma mark Application Delegate Methods

// Called with a CMD-Q
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
{
    // Cancel terminate if pref set
    if ([MyPreferencesController runInBackground])
    {
        [self switchToBackgroundMode];
        return NSTerminateCancel;
    } 
    return NSTerminateNow;
}

// Called when all windows closed
- (BOOL) applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender
{
    if ([MYPreferencesController runInBackground]) {
        // This check is necessary to avoid calling switchToBGmode twice on a quit
        if (![NSApp activationPolicy] == NSApplicationActivationPolicyAccessory)
            [self switchToBackgroundMode];
        return NO;
    } else {
        return YES;
    }
}

// Called if the app is in accessory mode and the user activates it through the dock or by
//   clicking a userNotification or trying to open the app
- (BOOL) applicationShouldHandleReopen:(NSApplication *)sender hasVisibleWindows:(BOOL)flag
{
    if (!self.wincon) {
        self.wincon = [[MYMainWindowController alloc] initWithWindowNibName:@"MainWindow"];
    }

    // This ensures that the dock icon comes back
    [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];

    // Show the window
    [self.wincon showWindow:NSApp];
    [self.wincon.window makeKeyAndOrderFront:NSApp];
    return YES;
}

2016年10月6日添加的注释,因为这个问题引起了一些关注:

有一个旧答案回答了这个问题。它对更改历史进行了很好的讨论,但缺乏示例代码。

最后,这个答案和问题完全缺少LSUIElement关键字,这是一个历史上的OSX plist设置,用于此类型的应用程序。如上面的答案和这个更近期的问题所述,应该考虑LSUIElement已被弃用。如果你发现了一篇提到它的旧博客文章,希望你找到更近期的代码示例,建议根本不使用它。


谢谢Steve。这段代码有助于实现所需的功能。 - Kishore
真是太棒了!谢谢。 - Duck

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接