Transform LSUIElement to foreground application

て烟熏妆下的殇ゞ 提交于 2020-01-05 19:12:11

问题


I have an app which must run all the time (if the user agree whit this).

When the user quit the app, I transform the foreground app into a LSUIElement (the app only has a menu bar icon, the dock icon and the menu disappear).

I have an options in the menu item which works ok and transform the LSUIElement into a foreground app (I use the functions [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular] and [NSApp activateIgnoringOtherApps:YES]).

My problem appear when the user double click on the app. I use again the [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular] in the delegate method applicationWillUnhide:(NSNotification *)notification, and all works well except the menu which doesn't appear. If I go to another app, and then I came back the menu appear. I try different methods but I wasn't able to find a good one.

I want to know is a delegate method which is called when the user double clicks on the app, or what is the function from NSApplication which is called in that moment, because I think using the setActivationPolicy: in the applicationWillUnhide function is to late.


回答1:


To transform a normal application to a LSUIElement I use

ProcessSerialNumber psn = { 0, kCurrentProcess };
TransformProcessType(&psn, kProcessTransformToUIElementApplication);

And to change it back to foreground :

ProcessSerialNumber psn = { 0, kCurrentProcess };
TransformProcessType(&psn, kProcessTransformToForegroundApplication);



回答2:


Here is the answer. I have already done the hide/show before I find this question. And this question inspired me to the final answer.

Here what the below code does:

  1. when app starts, app shows in dock and a menubar item shows.
  2. when user clicks the menubar item, app hides and remove from dock.
  3. when user clicks again, the app shows back to dock.
  4. if the app is hidden and user opens the app again from double click or launchpad, the app shows again in dock.
  5. if the app is not hidden but obscured by other apps, clicking the menubar item or relaunching it will put the app to the front.
  6. when a user click the close button on the window, the app removed from dock.
  7. when a user quit the app by cmd+q or from file menu, the app quits and the menubar item quits as well.

I have removed other code that is not directly related.

Other things you may notice:

  1. LSUIElement is not set or set to NO for my Info.plist. If you want to set to yes. You need to set no initiate view controller in storyboard and construct from the window controller yourself.
  2. You will also to deal the logic from left mouse click on menubar item, as you doesn't has the window from the very beginning.

Codes:

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


来源:https://stackoverflow.com/questions/12897214/transform-lsuielement-to-foreground-application

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!