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

时间:2016-12-27 06:24:48

标签: swift macos cocoa background-process menubar

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

但是,我还有一个用户可以从状态栏菜单打开的首选项窗口。以下是我如何打开它:

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

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

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

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

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

谢谢!

5 个答案:

答案 0 :(得分:2)

这是我现在的解决方法解决方案:

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

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

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

答案 1 :(得分:2)

基于OP中的修复(或解决方法)。关键确实是要切换出并返回焦点(感谢@Daniel!)。

一些小改进:

无需强制展开,无需设置ActivationPolicy并调用两次Activate。

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

答案 2 :(得分: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演示:

Video link

答案 3 :(得分:0)

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

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

答案 4 :(得分:0)

我找到了另一种解决方案,该解决方案不涉及将 Dock 聚焦和向后聚焦,这会导致应用退出/进入焦点时出现闪烁。 这将激活策略设置为禁止,然后在下一个勾号中常规:

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