macOS 菜单栏应用程序:主菜单未显示

16

我有一个状态栏应用程序,运行在菜单栏中。因此我在info.plst文件中将Application is agent (UIElement)设置为true。这样就不会出现我的应用程序的Dock图标和菜单栏。

然而,我还有一个偏好窗口,用户可以从状态栏菜单中打开。这是我打开它的方法:

if (!NSApp.setActivationPolicy(.regular)) {
    print("unable to set regular activation policy")
}
NSApp.activate(ignoringOtherApps: true)
if let window = preferencesWindowController.window {
    window.makeKeyAndOrderFront(nil)
}

窗口按预期显示,但应用程序的主菜单栏(如文件编辑等)未显示。只有当我点击另一个应用程序并回到我的应用程序时,菜单栏才会显示。

我注意到,如果我将 info.plist 中的值更改为 false,并在 applicationDidFinishLaunching() 中使用 NSApp.setActivationPolicy(.accessory),结果相同。但是,如果我在 applicationDidFinishLaunching() 调用后几毫秒内使用计时器调用 NSApp.setActivationPolicy(.accessory),它可以正常工作,并且主菜单栏按预期显示。但是,这样做的副作用是应用程序图标会在几秒钟内弹出到 dock 中(直到计时器被触发),这不是良好的用户体验。

有没有人有什么其他建议?当我切换活动应用程序时,发生了什么事情,而我在代码中没有做的事情?

我正在使用 macOS 10.12.2 (16C67)上的 Version 8.2.1 (8C1002)。

谢谢!


窗口如预期一样显示。我假设该窗口也成为了第一响应者,看起来并不像在后台或其他地方。正确吗? - idmean
我也遇到了我的应用程序的这个问题。你不是一个人 :) - Bob
5个回答

7
这是我目前的解决方案:

如我在问题中所写,如果我点击其他应用程序并返回到我的应用程序,则菜单栏将显示。当我尝试显示首选项窗口时,我正在模拟这种情况:

    NSApp.setActivationPolicy(.regular)
    NSApp.activate(ignoringOtherApps: true)
    window.makeKeyAndOrderFront(nil)

   if (NSRunningApplication.runningApplications(withBundleIdentifier: "com.apple.dock").first?.activate(options: []))! {
       let deadlineTime = DispatchTime.now() + .milliseconds(200)
       DispatchQueue.main.asyncAfter(deadline: deadlineTime) {                 
       NSApp.setActivationPolicy(.regular)
            NSApp.activate(ignoringOtherApps: true)
       }
   }

这并不是一个完美的解决方案。如果我找不到更好的解决方案,我将提交一个错误报告。


我遇到了同样的问题,但是我还没有找到可行的解决方案。你在哪里运行上面的代码?是在AppDelegate的applicationDidFinishLaunching函数中吗? - CyclingBloke
你好!你有没有找到更好的解决方案? - Dany Joumaa
不,我没有。抱歉。 - Daniel

2

根据原帖中的修复方法(或解决方法),关键是要切换到焦点并返回(感谢@Daniel!)。

一些小改进:

没有强制展开,不需要设置ActivationPolicy并调用两次激活。

NSApp.setActivationPolicy(.regular)
window.makeKeyAndOrderFront(nil)    

NSRunningApplication.runningApplications(withBundleIdentifier: "com.apple.dock").first?.activate(options: [])

OperationQueue.current?.underlyingQueue?.asyncAfter(deadline: .now() + .milliseconds(200), execute: {
    NSApp.activate(ignoringOtherApps: true)
})

我使用这个来解决一个问题,即当我使用应用程序图标(保存在 dock 中)重新打开先前隐藏以进入状态栏应用程序状态的应用程序窗口时,会锁定与菜单栏的交互。 - JMiguel

1
我找到了另一种解决方案,它不涉及将 dock 和 back 置于焦点,这会在应用程序退出/进入焦点时导致闪烁。 这将激活策略设置为禁止,然后在下一个 tick 中设置为常规:
if NSApp.activationPolicy() != .regular {
  NSApp.setActivationPolicy(.prohibited)
  DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + .milliseconds(200)) {
      NSApp.setActivationPolicy(.regular)
      NSApp.activate(ignoringOtherApps: true)
      window.makeKeyAndOrderFront(nil)
  }
} else {
    // activation policy was already regular, just do normal window opening
    NSApp.activate(ignoringOtherApps: true)
    window.makeKeyAndOrderFront(nil)
}

0

我可能找到了另一种解决方法(这在 macOS 10.15 上仍然是一个问题)。我在这里找到了一个类似的问题和解决方案。这个想法是显示和隐藏当前应用程序的菜单栏。我需要运行它2个不同的运行循环,但它可以工作。以下是解决方案:

OperationQueue.main.addOperation {
    NSMenu.setMenuBarVisible(false)
    OperationQueue.main.addOperation {
        NSMenu.setMenuBarVisible(true)
    }
}

谢谢您的建议。不幸的是,菜单栏出现了,但是点击菜单栏上的项目没有反应。 - Daniel

0

我尝试使用苹果脚本,看起来效果符合预期。

但是,在通过按键关闭后重新打开一个新窗口(短时间间隔)后,菜单将保持选定状态。

if NSApp.activationPolicy() == .accessory {
    NSApp.setActivationPolicy(.regular)
} else {
    var errorDict: NSDictionary?
    NSAppleScript(source: """
        tell application "Dock" to activate current application
        """)?.executeAndReturnError(&errorDict)
    if errorDict != nil{
        print(errorDict!)
        // error executing apple script
        NSApp.activate(ignoringOtherApps: true)
    }
}

window?.makeKeyAndOrderFront(self)

GIF演示:

视频链接


1
感谢您的回答。请注意:Apple Script 在 Mac App Store 上无法使用。我认为,如果您的应用程序被沙盒化,它甚至根本无法工作。 - Daniel
注意 @Daniel - Duke Fredrick

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