Modal NSWindow's behavior changed?

My app use modal window opened by NSApp.runModal(for: NSWindow).

This program works in macOS 13, but nothing happens when I press the button(Button("close ModalWindow")) in macOS 14.0 Sonoma.

Why is it not working in macOS 14 ?

import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
            Button("runModal") {
                let window = ModalWindow(
                    contentRect: NSRect(x: 20, y: 20, width: 500, height: 350),
                    styleMask: [.titled, .closable, .miniaturizable, .resizable],
                    backing: .buffered,
                    defer: false)
                window.isReleasedWhenClosed = false
                window.title = NSLocalizedString("subView", comment: "")
                window.contentView = NSHostingView(rootView: SubView())
                NSApp.runModal(for: window)
            }
            .buttonStyle(.borderedProminent)
            .tint(.accentColor)
        }
        .padding(10)
        .frame(minWidth: 150, maxWidth: .infinity, minHeight: 60, maxHeight: 60, alignment: .center)
    }
}

#Preview {
    ContentView()
}

final class ModalWindow: NSWindow {
    override func becomeKey() {
        super.becomeKey()
        level = .modalPanel
    }
    override func close() {
        super.close()
        NSApp.stopModal()
    }
}

struct SubView: View {
    var body: some View {
        HStack {
            Button("close ModalWindow") {
                NSApplication.shared.keyWindow?.close()
            }
        }
        .padding(10)
        .frame(minWidth: 150, maxWidth: .infinity, minHeight: 60, maxHeight: 60, alignment: .center)
    }
}

Instead of using runModal, use beginSheet on the window. beginSheet starts a document-modal session and presents it. Since it presents a window, first create an NSWindow (here named windowView) from the NSHostingController with the root view of your SwiftUI view.

The ModalWindow (window) needs to be passed into the SwiftUI view in order to close it.

After creating windowView, you can then call window.beginSheet on windowView. I have attached this code below. You also no longer need to set window.contentView = nsView since we are now using beginSheet instead of running the modal.

//ContentView
     let nsView = NSHostingController(rootView: SubView(window: window))
     let windowView = NSWindow(contentViewController: nsView)  
      window.beginSheet(windowView) { _ in
             //completion handler
   }

To close the window when the button in SubView is clicked, call close() on the NSWindow that you passed into the view. This will trigger the close function that is overridden in ModalWindow, and end the sheet due to the self.endSheet(self) call.

//ModalWindow
  override func close() {
        super.close()
        self.endSheet(self)
    }

//SubView
  let window: NSWindow?
var body: some View {
Button("close ModalWindow") {
                window?.close()
            }
}

Full code:

import SwiftUI

struct ContentView: View {
    
    var body: some View {
        VStack {
            Button("runModal") {
                let window = ModalWindow(
                    contentRect: NSRect(x: 20, y: 20, width: 500, height: 350),
                    styleMask: [.titled, .closable, .miniaturizable, .resizable],
                    backing: .buffered,
                    defer: false)
                window.isReleasedWhenClosed = false
                
                window.title = NSLocalizedString("subView", comment: "")
          
                let nsView = NSHostingController(rootView: SubView(window: window))
                
                let windowView = NSWindow(contentViewController: nsView)
            
                window.beginSheet(windowView) { _ in
                    //completion handler
                }
                
            }
            .buttonStyle(.borderedProminent)
            .tint(.accentColor)
        }
        .padding(10)
        .frame(minWidth: 150, maxWidth: .infinity, minHeight: 60, maxHeight: 60, alignment: .center)
    }
}


 class ModalWindow: NSWindow {
    override func becomeKey() {
        super.becomeKey()
        
    }
     override func close() {
         super.close()
         self.endSheet(self)
         
     }
     
}

struct SubView: View {
    let window: NSWindow?
    
    var body: some View {
        HStack {
            Button("close ModalWindow") {
                window?.close()
            }
            
        }
        .padding(10)
        .frame(minWidth: 150, maxWidth: .infinity, minHeight: 60, maxHeight: 60, alignment: .center)
    }
}
Modal NSWindow's behavior changed?
 
 
Q