Skip to content

Instantly share code, notes, and snippets.

@davidbalbert
Last active December 16, 2023 18:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save davidbalbert/3fbb7f88e3c964062b917557d969923d to your computer and use it in GitHub Desktop.
Save davidbalbert/3fbb7f88e3c964062b917557d969923d to your computer and use it in GitHub Desktop.
//
// OnWindowClose.swift
//
// Created by David Albert on 12/16/23.
//
import SwiftUI
import AppKit
struct WindowReader: NSViewRepresentable {
class View: NSView {
static let didMoveToWindowNotification: NSNotification.Name = NSNotification.Name("viewDidMoveToWindow")
override func viewDidMoveToWindow() {
super.viewDidMoveToWindow()
NotificationCenter.default.post(name: Self.didMoveToWindowNotification, object: self)
}
}
class Coordinator {
var observer: AnyObject?
}
func makeCoordinator() -> Coordinator {
Coordinator()
}
@Binding var window: NSWindow?
func makeNSView(context: Context) -> View {
let view = View()
let observer = NotificationCenter.default.addObserver(forName: View.didMoveToWindowNotification, object: view, queue: nil) { _ in
window = view.window
}
context.coordinator.observer = observer
return view
}
func updateNSView(_ nsView: View, context: Context) {
}
static func dismantleNSView(_ nsView: View, coordinator: Coordinator) {
guard let observer = coordinator.observer else {
return
}
NotificationCenter.default.removeObserver(observer)
}
}
struct OnWindowClose: ViewModifier {
@State var window: NSWindow?
@State var observer: AnyObject?
let action: () -> Void
func body(content: Content) -> some View {
content
.background {
WindowReader(window: $window)
}
.onChange(of: window) {
// Can't just use onReceive and NotificationCenter.publisher(for:) because window starts as nil, and we don't want
// to register an observer for every window.
if let observer {
NotificationCenter.default.removeObserver(observer)
self.observer = nil
}
if let window {
observer = NotificationCenter.default.addObserver(forName: NSWindow.willCloseNotification, object: window, queue: nil) { _ in
action()
}
}
}
}
}
extension View {
func onWindowClose(perform action: @escaping () -> Void) -> some View {
modifier(OnWindowClose(action: action))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment