Skip to content

Instantly share code, notes, and snippets.

@kdeda
Last active March 10, 2024 13:31
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kdeda/d7c2f6aafc066793e8cc8f4e398d0814 to your computer and use it in GitHub Desktop.
Save kdeda/d7c2f6aafc066793e8cc8f4e398d0814 to your computer and use it in GitHub Desktop.
Open/Close NSWindow from SwiftUI.View
import SwiftUI
/**
hack to avoid crashes on window close, and remove the window from the
NSApplication stack, ie: avoid leaking window objects
*/
fileprivate final class WindowDelegate: NSObject, NSWindowDelegate {
func windowShouldClose(_ sender: NSWindow) -> Bool {
NSApp.removeWindowsItem(sender)
return true
}
deinit {
NSLog("deallocated ...")
}
}
public extension View {
func openInNewWindow(_ introspect: @escaping (_ window: NSWindow) -> Void) {
let windowDelegate = WindowDelegate()
let rv = NSWindow(
contentRect: NSRect(x: 0, y: 0, width: 320, height: 320),
styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
backing: .buffered,
defer: false
)
rv.isReleasedWhenClosed = false
rv.title = "New Window"
// who owns who :-)
rv.delegate = windowDelegate
rv.contentView = NSHostingView(rootView: self)
introspect(rv)
rv.center()
rv.makeKeyAndOrderFront(nil)
}
}
import SwiftUI
@main
struct WindowVanillaSwiftUIApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView: View {
var body: some View {
Button(action: {
ChildView().openInNewWindow { window in
window.title = "Window title"
}
}) {
Image(systemName: "paperplane")
}
.padding()
}
}
struct ChildView: View {
var body: some View {
VStack {
Spacer()
HStack {
Spacer()
Text("Hello World")
.padding()
Spacer()
}
Spacer()
HStack {
Spacer()
Button(action: {
NSApp.keyWindow?.close()
}) {
Text("Close")
}
}
.padding()
}
.frame(minWidth: 640, minHeight: 480)
}
}
struct ChildView_Previews: PreviewProvider {
static var previews: some View {
ChildView()
}
}
@kdeda
Copy link
Author

kdeda commented Jun 2, 2023

If you are using TCA from pointfree.co
You can pass in a scoped store to that ChildView

        Button(action: {
            ChildView(store: self.store.scope(...)).openInNewWindow { window in
                window.title = "Window title"
            }
        }) {
            Image(systemName: "paperplane")
        }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment