将 LSUIElement 转换为前台应用程序
Posted
技术标签:
【中文标题】将 LSUIElement 转换为前台应用程序【英文标题】:Transform LSUIElement to foreground application 【发布时间】:2012-10-15 13:58:33 【问题描述】:我有一个必须一直运行的应用程序(如果用户同意的话)。
当用户退出应用程序时,我将前台应用程序转换为 LSUIElement(应用程序只有一个菜单栏图标,停靠图标和菜单消失)。
我在菜单项中有一个选项可以正常工作并(我使用函数 [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]
和 [NSApp activateIgnoringOtherApps:YES]
)。
当用户双击应用程序时出现我的问题。我在委托方法applicationWillUnhide:(NSNotification *)notification
中再次使用[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]
,除了没有出现的菜单外,一切都很好。如果我去另一个应用程序,然后我回来菜单就会出现。我尝试了不同的方法,但找不到好的方法。
我想知道当用户双击应用程序时调用的委托方法,或者在那一刻调用的NSApplication
的函数是什么,因为我认为在@ 中使用setActivationPolicy:
987654327@函数来晚了。
【问题讨论】:
你试过从applicationShouldHandleReopen:
调用它吗?
是的,我试过了,但没有成功。
而在 applicationDidUnhide ?
我遇到了同样的问题。你找到解决办法了吗?
【参考方案1】:
为了将普通应用程序转换为我使用的 LSUIElement
ProcessSerialNumber psn = 0, kCurrentProcess ;
TransformProcessType(&psn, kProcessTransformToUIElementApplication);
并将其更改回前台:
ProcessSerialNumber psn = 0, kCurrentProcess ;
TransformProcessType(&psn, kProcessTransformToForegroundApplication);
【讨论】:
据我所知TransformProcessType
和NSApp setActivationPolicy:
具有相同的效果。【参考方案2】:
这就是答案。在找到这个问题之前,我已经完成了隐藏/显示。而这个问题启发了我最终的答案。
以下代码的作用如下:
-
当应用启动时,应用会显示在 Dock 中并显示一个菜单栏项。
当用户单击菜单栏项时,应用会隐藏并从停靠栏中移除。
当用户再次点击时,应用会重新显示到停靠栏。
如果应用程序被隐藏并且用户通过双击或启动板再次打开应用程序,应用程序会再次显示在停靠栏中。
如果应用没有隐藏但被其他应用遮住,单击菜单栏项或重新启动它会将应用置于最前面。
当用户单击窗口上的关闭按钮时,应用程序会从 Dock 中移除。
当用户通过 cmd+q 或从文件菜单退出应用程序时,应用程序退出,菜单栏项也退出。
我已经删除了其他不直接相关的代码。
您可能会注意到的其他事项:
-
对于我的 Info.plist,LSUIElement 未设置或设置为 NO。如果你想设置为是。您需要在 storyboard 中设置 noinitial view controller 并自己从窗口控制器构造。
您还将通过鼠标左键单击菜单栏项来处理逻辑,因为您从一开始就没有窗口。
代码:
import Cocoa
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate
private let statusItem = NSStatusBar.system.statusItem(withLength:NSStatusItem.squareLength)
weak private var window:NSWindow? = nil
func applicationDidFinishLaunching(_ aNotification: Notification)
setupMenubarTray()
self.window = NSApp.orderedWindows.first
NotificationCenter.default.addObserver(self, selector: #selector(windowWillClose(_:)), name: NSWindow.willCloseNotification, object: self.window!)
func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool
if !window!.isVisible
activeApp()
return false
return true
extension AppDelegate
@objc func windowWillClose(_ noti:Notification)
removeFromDock()
private func showInDock()
NSApp.setActivationPolicy(.regular)
private func removeFromDock()
NSApp.setActivationPolicy(.accessory)
// MARK: - setup menubar button
extension AppDelegate
private func setupMenubarTray()
guard let button = statusItem.button else
fatalError()
setTrayIcon(for:button)
button.action = #selector(mouseLeftButtonClicked)
private func setTrayIcon(for button:NSStatusBarButton)
let useMonochromeIcon = UserDefaults.standard.bool(forKey: DefaultsKey.useMonochromeIcon.key)
button.image = NSImage(imageLiteralResourceName: useMonochromeIcon ? "MonochromeIcon" : "TrayIcon")
@objc private func mouseLeftButtonClicked()
if NSApp.isHidden || !window!.isKeyWindow
self.activeApp()
else
self.hide()
private func activeApp()
showInDock()
window?.makeKeyAndOrderFront(nil)
NSApp.activate(ignoringOtherApps: true)
checker.sendNotification()
private func hide()
removeFromDock()
NSApp.hide(nil)
【讨论】:
以上是关于将 LSUIElement 转换为前台应用程序的主要内容,如果未能解决你的问题,请参考以下文章
java在后台如何将前台传过来的json格式数据转换为map?
LSUIElement 的行为与 activateIgnoringOtherApps 不一致