How do we decide what windows open on launch?
How to open a window in SwiftUI?
How do we decide what windows open on launch?
Code Block swift @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate var body: some Scene { Settings { } }
Code Block swift class AppWindow: NSWindow { init() { super.init(contentRect: NSRect(x: 0, y: 0, width: 480, height: 300), styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView], backing: .buffered, defer: false) makeKeyAndOrderFront(nil) isReleasedWhenClosed = false styleMask.insert(NSWindow.StyleMask.fullSizeContentView) title = "title placeholder" contentView = NSHostingView(rootView: ContentView()) } }
Code Block swift var body: some Scene { WindowGroup { ContentView().environmentObject(appState) } WindowGroup("File List") { if appState.showFileList { FileListView().environmentObject(appState) } } }
Code Block @main struct TestApp: App { var body: some Scene { WindowGroup { RootView() } } } var useWindow1: Bool = true struct RootView: View { var body: some View { Group { if useWindow1 { Window1() } else { Window2() } } } }
Code Block NSApp.mainWindow?.windowController?.newWindowForTab(nil)
Code Block struct ContentView: View { @Environment(\.openURL) var openURL var body: some View { VStack { Button("Open Viewer") { if let url = URL(string: "myappname://viewer") { openURL(url) } } Text("Hello, world!") } .padding() } } struct Viewer: View { var body: some View { Text("Viewer") } }
Code Block @main struct GroupDefaultsTestApp: App { var body: some Scene { WindowGroup { ContentView() } WindowGroup("Viewer") { // other scene Viewer() } .handlesExternalEvents(matching: Set(arrayLiteral: "*")) } }
This is the best solution for macOS 13 or earlier if we want to open a new window in a SwiftUI macOS app. Thanks for your sharing and I get a lot from your professional answer. For macOS 13 and later, we can use openWindow method to solve this problem more elegantly.
Tested on Xcode 13 beta, SwiftUI 3.0
After having being in this situation, I Frankensteined some answers that where all over the internet and this works for me:
On the @main (MyAppApp) file add the amount of WindowGroup("Window Name")
you need:
import SwiftUI
@main
struct MyAppApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
WindowGroup("Second Window") {
SecondWindow()
}.handlesExternalEvents(matching: Set(arrayLiteral: "SecondWindow"))
WindowGroup("Third Window") {
ThirdWindow()
}.handlesExternalEvents(matching: Set(arrayLiteral: "ThirdWindow"))
}
What to place in every WindowGroup
?:
WindowGroup("SecondWindow") /*Any name you want to be displayed at the top of the window.*/ {
SecondWindow() //View you want to display.
}.handlesExternalEvents(matching: Set(arrayLiteral: "SecondWindow")) //Name of the view without ().
Now, at the end of the MyAppApp file (outside of the struct MyAppApp: App
) add the following enum
:
enum OpenWindows: String, CaseIterable {
case SecondView = "SecondView"
case ThirdView = "ThirdView"
//As many views as you need.
func open(){
if let url = URL(string: "myapp://\(self.rawValue)") { //replace myapp with your app's name
NSWorkspace.shared.open(url)
}
}
}
Add the following to your Info.plist
Replace myapp with your app's name.
Usage:
Button(action: {
OpenWindows.SecondView.open()
}){
Text("Open Second Window")
}
I had to enter the URL Schemes under "URL Types" in my project target's settings. Then it worked.
But the window that got opened is very tiny. How to set a frame or size for the window?
Thank you so much. This worked like a charm
NSWorkspace.shared.open is not aailable in Mac Catalyst. Any solution?
I have found this also to work. Although I think SwiftUI needs a much better way of supporting multiple macOS Windows.
In your AppStruct, although any persistent struct or class can hold a window.
Limitations: No idea how to let the system determine the window position or whether it will autosave window information.
struct Radio_2App: App {
let someWindow = NSWindow( contentRect: NSRect(x: 0, y: 0, width: 580, height: 300), styleMask: [.titled, .closable], backing: .buffered, defer: false)
var body: some Scene {
WindowGroup() {
SomeContentView()
}
func openWindow() {
someWindow.contentView = NSHostingView(rootView: SomeOtherView())
self.someWindow.makeKeyAndOrderFront(nil)
}
}
view extension:
extension View {
private func newWindowInternal(title: String, geometry: NSRect, style: NSWindow.StyleMask, delegate: NSWindowDelegate) -> NSWindow {
let window = NSWindow(
contentRect: geometry,
styleMask: style,
backing: .buffered,
defer: false)
window.center()
window.isReleasedWhenClosed = false
window.title = title
window.makeKeyAndOrderFront(nil)
window.delegate = delegate
return window
}
func openNewWindow(title: String, delegate: NSWindowDelegate, geometry: NSRect = NSRect(x: 20, y: 20, width: 640, height: 480), style:NSWindow.StyleMask = [.titled, .closable, .miniaturizable, .resizable]) {
self.newWindowInternal(title: title, geometry: geometry, style: style, delegate: delegate).contentView = NSHostingView(rootView: self)
}
}
call with:
Text("This is a swiftui text").openNewWindow(...)
Make sure to keep a strong reference to the window delegate, the window will keep only a weak ref.
This worked perfectly for me! Thank you so much!