iphaaw
iphaaw

Reputation: 7204

SwiftUI: Run code when window closed macOS

I have opened my window in the traditional way, but I want to run some code when the window is closed by clicking the close box (red button). Is there a good way of doing this?

func openMyWindow()
{
    myWindow = (NSWindow(
    contentRect: NSRect(x: 100, y: 100, width: 100, height: 600),
    styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
        backing: .buffered, defer: false))
    myWindow!.contentView = NSHostingView(rootView: MyWindowView())
        myWindow!.makeKeyAndOrderFront(nil)
}

Upvotes: 8

Views: 1670

Answers (2)

davidev
davidev

Reputation: 8557

Great question.. I struggled on that a time ago as well.

You can make your AppDelegate or class conform to NSWindowDelegate protocol.

Then pass self as the delegate of the window

myWindow.delegate = self

And implement following function to be notified by the closing action

func windowShouldClose(_ sender: NSWindow) -> Bool
{}

Edit:

When you are using SwiftUI life cycle, you can add an AppDelegate. You can also implement your own WindowManager class. Here is an example:

class WindowManager : NSObject, NSWindowDelegate {
    var popUpWindow : NSWindow? = nil
    
    override init() {}
    
    func openMyWindow()
    {
        popUpWindow = (NSWindow(
        contentRect: NSRect(x: 100, y: 100, width: 100, height: 600),
        styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
            backing: .buffered, defer: false))
        popUpWindow!.contentView = NSHostingView(rootView: PopUpView())
        popUpWindow!.makeKeyAndOrderFront(nil)
        popUpWindow?.delegate = self
    }
    
    func windowShouldClose(_ sender: NSWindow) -> Bool
    {
        print("Window will close")
        return true
    }
}

struct PopUpView : View {
    var body: some View {
        Text("This is a new popup view")
    }
}

Create and hold that class in your struct _: App and open Windows via this Manager.

@main
struct macSwiftUICycleApp: App {
    let persistenceController = PersistenceController.shared

    let windowManager : WindowManager = WindowManager() //<< Here keep the instance of your WindowManager
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(\.managedObjectContext, persistenceController.container.viewContext)
                .onAppear {
                    windowManager.openMyWindow() //<< Just a test to open another window on startup
                }
        }
    }
}

Upvotes: 7

Asperi
Asperi

Reputation: 258541

If the code you want to run is independent of context then the simplest solution is to replace close action for window and run your code there, like

func openMyWindow()
{
    // ... other your code

    myWindow!.contentView = NSHostingView(rootView: MyWindowView())

    let closeButton = myWindow!.standardWindowButton(.closeButton)  // << !!
    closeButton?.action = #selector(NSWindow.doMyClose(_:))           // << !!

    myWindow!.makeKeyAndOrderFront(nil)
}

extension NSWindow {
    @objc
    func doMyClose(_ sender: Any?) {
        // ... run your code here

        self.close()  // << make standard window close

        // ... or here 
    }
}

if you want to execute some closure with captured variables, then the approach is the same but with custom handler class which would hold window and closure to perform.

Upvotes: 1

Related Questions