I started working on an app for MacOS and got stuck on several issues. One of them is the following.
I have a UIElement app.
It's initialized in the following way:
@main
struct MyApp: App {
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
Settings {
EmptyView()
}
}
}
In my AppDelegate.swift I have:
import SwiftUI
class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
static private(set) var instance: AppDelegate!
var settingsWindow: NSWindow!
lazy var statusBarItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
let menu = AppMenu()
func applicationDidFinishLaunching(_ notification: Notification) {
statusBarItem.button?.image = NSImage(
systemSymbolName: "circle",
accessibilityDescription: "My App"
)
statusBarItem.menu = menu.createMenu()
}
@objc func openSettingsWindow() {
if settingsWindow == nil {
let settingsView = SettingsView()
settingsWindow = NSWindow(
contentRect: NSRect(x: 20, y: 20, width: 480, height: 300),
styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
backing: .buffered,
defer: false
)
settingsWindow.center()
settingsWindow.title = "Settings"
settingsWindow.isReleasedWhenClosed = false
settingsWindow.contentView = NSHostingView(rootView: settingsView)
}
settingsWindow.makeKeyAndOrderFront(nil)
}
}
And the last, the menu is built like this:
import SwiftUI
class AppMenu: NSObject {
let menu = NSMenu()
func createMenu() -> NSMenu {
// ...
let settingsMenuItem = NSMenuItem(title: "Settings", action: #selector(settings), keyEquivalent: ",")
settingsMenuItem.target = self
menu.addItem(settingsMenuItem)
// ...
return menu
}
@objc func settings(sender: NSMenuItem) {
NSApp.sendAction(#selector(AppDelegate.openSettingsWindow), to: nil, from: nil)
}
}
But when I open the app in the menu bar and hit Settings the window with SettingsView
is displayed randomly (I'm not sure what is exactly the reason for it being opened and not).
In the log section of Xcode I see warnings:
2023-05-29 21:39:57.172266+0200 My App[2211:1474025] [Window] Warning: Window NSMenuWindowManagerWindow 0x7fb8d4e21640 ordered front from a non-active application and may order beneath the active application's windows.
2023-05-29 21:40:02.197549+0200 My App[2211:1474025] [Window] Warning: Window NSWindow 0x7fb8d3717790 ordered front from a non-active application and may order beneath the active application's windows.
The first warning is fired when I hit the app icon in the menu bar (every time) and the second – when Settings is clicked.
My goal is to have an app without a window in docker but with an opportunity to open different windows via its menu bar items.
I think your settings window is opening each time, but calling from your status bar menu does not activate your app. Thus, it's hidden under other windows.
Option 1: Activate your app
settingsWindow.makeKeyAndOrderFront(nil)
NSApp.activate(ignoringOtherApps: true) // add this
Option 2: Raise window level
settingsWindow.level = NSWindow.Level.init(rawValue: 1) // add this
settingsWindow.makeKeyAndOrderFront(nil)
If you want an app without Dock icon, add this in your Info.plist:
LSUIElement = TRUE