有没有一种方法可以不在SwiftUI生命周期的macOS应用程序中显示窗口?

18

我希望在 macOS 上不显示 SwiftUI 应用程序的窗口。该应用程序使用 SwiftUI 的应用程序生命周期,并且仅在状态栏中运行。启动时显示一个窗口是不必要的。然而,我不确定如何避开 WindowGroup。没有 EmptyScene,在 WindowGroup 中放置 EmptyView 当然会创建一个空白窗口。

@main
struct MyApp: App {
    @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

我基本上只需要应用程序委托。我猜使用默认的AppKit生命周期更有意义,但如果有一种方法可以使用SwiftUI的生命周期,那就太好了。

4个回答

21

在您的AppDelegate中,在applicationDidFinishLaunching中执行以下操作:

func applicationDidFinishLaunching(_ notification: Notification) {
    // Close main app window
    if let window = NSApplication.shared.windows.first {
        window.close()
    }
    
    // Code continues here...
}
例如:
class AppDelegate: NSObject, NSApplicationDelegate {
    var popover = NSPopover.init()
    var statusBar: StatusBarController?
    
    func applicationDidFinishLaunching(_ notification: Notification) {
        // Close main app window
        if let window = NSApplication.shared.windows.first {
            window.close()
        }
        
        // Create the SwiftUI view that provides the contents
        let contentView = ContentView()

        // Set the SwiftUI's ContentView to the Popover's ContentViewController
        popover.contentSize = NSSize(width: 160, height: 160)
        popover.contentViewController = NSHostingController(rootView: contentView)
        
        // Create the Status Bar Item with the above Popover
        statusBar = StatusBarController.init(popover)
    }
    
}

它在我这里运作正常。谢谢你,你让我的一天变得美好。 - Hakan Turkmen
这似乎是唯一可行的解决方法。 - Owen Zhao
谢谢。我发现使用这个东西时我的弹出内容在第一次运行时加载得非常慢。我把它改成了 window.orderOut(self)。这似乎允许更快地加载内容。 - markhunte

9
该应用程序使用SwiftUI的应用程序生命周期,并且仅在状态栏中运行。
使用MenuBarExtra场景,在系统菜单栏中以原生SwiftUI生命周期渲染一个持久控件。(Xcode 14.2,macOS Ventura)
@main
struct MyStatubarMenuApp: App {
    var body: some Scene {
        // -- no WindowGroup required --
        // -- no AppDelegate needed --
        MenuBarExtra { 
            Text("Hello Status Bar Menu!")
            MyCustomSubmenu()
            Divider()
            Button("Quit") { NSApp.terminate(nil) }
        } label: {
            Image(systemName: "bolt.fill")
        }
    }
}

注意:

  • Info.plist中添加并设置Application is agent = YES,以使应用程序图标不显示在Dock上。
  • systemName: SF Symbols将在System Settings… > Appearance更改时自动切换外观。
  • @Environment(\.colorScheme) 当前无法在App/Scene级别更新

1
我使用相同的方法通过截图找到了这个页面:https://sarunw.com/posts/swiftui-menu-bar-app/ - Iman Mahmoudinasab

7

如果您的应用程序有设置(谁没有呢?),可以按照以下步骤进行:

@main
struct TheApp: App {
  @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

  var body: some Scene {
    Settings {
      SettingsView()
    }
  }
}


1
谢谢。如果你不想显示任何内容,只需使用Settings {}即可。 - undefined

6
你应该能够做到像这样的事情:
var body: some Scene {
  WindowGroup {
    ZStack {
      EmptyView()
    }
    .hidden()
  }
}

5
这不幸地也创建了一个空窗口 :/ - Kilian
啊,你说得对。已经更新了,请试试这个。 - mrtom
窗口菜单仍将显示那些窗口,即使它们没有可见的部分。 - user965972
在Info.plist中添加“Application is agent = YES”,就可以使其正常工作。谢谢! - Hrvoje
1
这是一个非常危险的解决方案,具有副作用。 - flymg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接