Reputation: 4189
I am using Big Sur and SwiftUI with the SwiftUI lifecycle. I want to implement an alert, where the user gets asked, if the application can be quit or not. How is this possible with SwiftUI? It should look like this:
Upvotes: 4
Views: 1836
Reputation: 4189
It's possible by using this code (this code opens the Alert
only in the key window):
import SwiftUI
import AppKit
class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
@Published var willTerminate = false
func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
// check, if at least one window is open:
if NSApplication.shared.windows.count == 0 {
// if no one is open, close it
return .terminateNow
}
// if one or more are open, set the willTerminate variable, so that the alert can be shown
self.willTerminate = true
// return a .terminateLater (to which we need to reply later!)
return .terminateLater
}
/// This method tells the application, that it should not close
func `continue`() {
NSApplication.shared.reply(toApplicationShouldTerminate: false)
}
/// This method closes the application
func close() {
NSApplication.shared.reply(toApplicationShouldTerminate: true)
}
}
@main
struct WindowShouldCloseApp: App {
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate: AppDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
And this is the ContentView.swift
:
import SwiftUI
struct ContentView: View {
@EnvironmentObject private var appDelegate: AppDelegate
@State private var window: NSWindow?
var body: some View {
Text("Hello, world!")
.padding()
.background(WindowAccessor(window: self.$window)) // access the window
.alert(isPresented: Binding<Bool>(get: { self.appDelegate.willTerminate && self.window?.isKeyWindow ?? false }, set: { self.appDelegate.willTerminate = $0 }), content: {
// show an alert, if the application should be closed
Alert(title: Text("Really close?"),
message: Text("Do you really want to close the application?"),
primaryButton: .default(Text("Continue"), action: { self.appDelegate.continue() }),
secondaryButton: .destructive(Text("Close"), action: { self.appDelegate.close() }))
})
}
}
// thanks to Asperi: https://stackoverflow.com/questions/63432700/how-to-access-nswindow-from-main-app-using-only-swiftui/63439982#63439982
struct WindowAccessor: NSViewRepresentable {
@Binding var window: NSWindow?
func makeNSView(context: Context) -> NSView {
let view = NSView()
DispatchQueue.main.async {
self.window = view.window
}
return view
}
func updateNSView(_ nsView: NSView, context: Context) {}
}
Upvotes: 4