简体   繁体   English

如何为 macOS 菜单栏应用启用自动启动?

[英]How enable autostart for a macOS menu bar app?

I am building an macOS app for the menu bar and it should automatically start with system start.我正在为菜单栏构建一个 macOS 应用程序,它应该随着系统启动而自动启动。 I started with implementing the autostart functionality for a standard window based macOS app following this tutorial this tutorial .我开始按照本教程的本教程为基于标准 window 的 macOS 应用程序实现自动启动功能。 I have我有

  • added a new target inside the main project (the helper app)在主项目(助手应用程序)中添加了一个新目标
  • changed skip install to yes for the helper app将帮助应用程序的skip install更改为yes
  • set the helper app to be a background only app将助手应用程序设置为仅后台应用程序
  • added a new copy file build phase to the main application to copy the helper application into the bundle向主应用程序添加了一个新的复制文件构建阶段,以将帮助应用程序复制到包中
  • linked the ServiceManagement.framework链接ServiceManagement.framework
  • Implemented the functionality in the app delegates, that the helper app gets launched with system start.在应用程序委托中实现了功能,即帮助应用程序在系统启动时启动。 After it has launched, it launches the main app (see the tutorial link for more info or the source code down below)启动后,它会启动主应用程序(有关更多信息,请参阅教程链接或下面的源代码)

That worked fine, the app launched automatically:) So I started changing the project, that the main application becomes a menu bar app.效果很好,应用程序自动启动:) 所以我开始更改项目,主应用程序变成了菜单栏应用程序。 However than, the app wouldn't auto launch anymore:/ Does someone have a solution for that?但是,该应用程序将不再自动启动:/有人对此有解决方案吗?

Heres the code of the app delegate of the main app:是主应用程序的应用程序委托的代码:

import Cocoa
import ServiceManagement

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)


func applicationDidFinishLaunching(_ aNotification: Notification) {

    statusItem.button?.title = "Test"
    statusItem.button?.target = self
    statusItem.button?.action = #selector(showWindow)


    // auto start
    let launcherAppId = "com.####.####Helper"
    let runningApps = NSWorkspace.shared.runningApplications
    let isRunning = !runningApps.filter { $0.bundleIdentifier == launcherAppId }.isEmpty

    SMLoginItemSetEnabled(launcherAppId as CFString, true)

    if isRunning {
        DistributedNotificationCenter.default().post(name: .killLauncher, object: Bundle.main.bundleIdentifier!)
    }
}

func applicationWillTerminate(_ aNotification: Notification) {
    // Insert code here to tear down your application
}

@objc func showWindow() {
    let storyboard = NSStoryboard(name: "Main", bundle: nil)
    guard let vc = storyboard.instantiateController(withIdentifier: "ViewController") as? ViewController else {
        fatalError("Unable to find main view controller")
    }

    guard let button = statusItem.button else {
        fatalError("Unable to find status item button")
    }

    let popover = NSPopover()
    popover.contentViewController = vc
    popover.behavior = .transient
    popover.show(relativeTo: button.bounds, of: button, preferredEdge: .maxY)

}


}

extension Notification.Name {
   static let killLauncher = Notification.Name("killLauncher")
}

And this is the app delegate of the helper app:这是帮助应用程序的应用程序委托:

import Cocoa

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {



func applicationDidFinishLaunching(_ aNotification: Notification) {
    let mainAppIdentifier = "com.####.####"
    let runningApps = NSWorkspace.shared.runningApplications
    let isRunning = !runningApps.filter { $0.bundleIdentifier == mainAppIdentifier }.isEmpty

    if !isRunning {
        DistributedNotificationCenter.default().addObserver(self, selector: #selector(self.terminate), name: .killLauncher, object: mainAppIdentifier)

        let path = Bundle.main.bundlePath as NSString
        var components = path.pathComponents
        components.removeLast()
        components.removeLast()
        components.removeLast()
        components.append("MacOS")
        components.append("####") //main app name

        let newPath = NSString.path(withComponents: components)

        NSWorkspace.shared.launchApplication(newPath)
    }
    else {
        self.terminate()
    }
}

func applicationWillTerminate(_ aNotification: Notification) {
    // Insert code here to tear down your application
}

@objc func terminate() {
    NSApp.terminate(nil)
}


}

extension Notification.Name {
  static let killLauncher = Notification.Name("killLauncher")
}

Thank you very much for your help:)非常感谢您的帮助:)

My code looks pretty much the same, except how I compose the path in the helper app:我的代码看起来几乎相同,除了我如何在帮助应用程序中编写路径:

var pathComponents = (Bundle.main.bundlePath as NSString).pathComponents
pathComponents.removeLast()
pathComponents.removeLast()
pathComponents.removeLast()
pathComponents.removeLast()
let newPath = NSString.path(withComponents: pathComponents)
NSWorkspace.shared.launchApplication(newPath)

Also, if I remember correctly, I had to make sure the Main.storyboard file still had the "Application Scene" with an Application object and an empty main menu.另外,如果我没记错的话,我必须确保 Main.storyboard 文件仍然具有“应用程序场景”,其中包含应用程序 object 和一个空的主菜单。

应用场景

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM