首页 > 解决方案 > SwiftUI 和 AppKit:使用关闭对话框询问应用是否允许退出

问题描述

我在 SwiftUI 生命周期中使用 Big Sur 和 SwiftUI。我想实现一个警报,询问用户是否可以退出应用程序。SwiftUI 怎么可能?它应该如下所示:

在此处输入图像描述

标签: swiftui

解决方案


可以使用此代码(此代码Alert仅在密钥窗口中打开):

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()
        }
    }
}

这是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) {}
}

推荐阅读