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

macOS menubar application: main menu not being displayed

我有一个状态栏应用程序,它在菜单栏中运行。因此我在info.plst中将Application is agent (UIElement)设置为true。这导致我的应用程序没有停靠栏图标和菜单栏。

不过,我还有一个偏好 window,用户可以从状态栏菜单中打开。这是我打开它的方式:

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

window 按预期显示,但应用程序的主菜单栏带有 FileEdit 等,没有出现。只有当我点击另一个应用程序并返回到我的应用程序时,才会显示菜单栏。

我注意到,如果我将 info.plst 中的值更改为 false 并在 applicationDidFinishLaunching() 中使用 NSApp.setActivationPolicy(.accessory),结果相同。但是,如果我在 applicationDidFinishLaunching() 被调用几毫秒后用计时器调用 NSApp.setActivationPolicy(.accessory),它会工作并且主菜单会按预期显示。然而,这有一个副作用,即应用程序图标会在 Dock 中弹出几秒钟(直到计时器被触发),这不是一个很好的用户体验。

有人知道我还能尝试什么吗?当我切换活动应用程序时发生了什么,我没有在代码中做?

我在 macOS 10.12.2 (16C67) 上使用版本 8.2.1 (8C1002)

谢谢!

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

正如我在问题中所写,如果我点击另一个应用程序并返回到我的应用程序,则会显示菜单栏。当我尝试显示偏好 window:

时,我正在模拟这个
    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)
       }
   }

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

我尝试使用苹果脚本,显示按预期工作。

Except, after closing by key strokes and then reopening a new window (short time interval), menu will stay selected.

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 演示:

Video link

基于 OP 的修复(或解决方法)。关键真的是切换和返回焦点(感谢@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)
})

我可能会为此找到另一个解决方案(这在 macOS 10.15 上仍然是一个问题)。我发现了一个类似的问题+解决方案here。这个想法是关于显示和隐藏当前应用程序的 menuBar。我需要 运行 它有 2 个不同的 运行 循环,但它起作用了。这是解决方案:

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

我找到了另一种解决方案,它不涉及对扩展坞和背面进行对焦,这会导致应用 exited/entered 对焦时出现闪烁。 这将激活策略设置为禁止,然后在下一个滴答中正常:

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)
}