You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI Mac Catalyst应用中如何实现键盘(Escape键)关闭对话框?

在SwiftUI Mac Catalyst应用中实现Escape键关闭对话框的方案

Mac上关闭对话框的惯用操作是按Escape键,但在SwiftUI Mac Catalyst应用中,尝试多种keyboardShortcut配置都无法实现该功能。测试环境为XCode 15.0.1、macOS 13.6、最低iOS部署版本16.0,示例代码如下:

import SwiftUI

struct ContentView: View {
    @State private var isSheetPresented = false
    
    var body: some View {
        VStack {
            Button("Show Sheet") {
                isSheetPresented.toggle()
            }
            .sheet(isPresented: $isSheetPresented) {
                SheetView(dismissAction: {
                    isSheetPresented = false
                })
            }
        }
    }
}

struct SheetView: View {
    var dismissAction: () -> Void
    
    var body: some View {
        VStack {
            Text("Hello")
            
            Button("Dismiss") {
                dismissAction()
            }
        }
        .keyboardShortcut("e", modifiers: .command) // works
        .keyboardShortcut(.escape, modifiers: .command) // does not work
        .keyboardShortcut("e",modifiers: []) // does not work
        .keyboardShortcut(.escape, modifiers: []) // does not work
        .keyboardShortcut(.cancelAction)  // escape, command w and command period do not work
    }
}

@main
struct testkeyboardApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

解决方案

方案一:使用原生环境变量绑定快捷键

替换自定义的dismissAction为SwiftUI内置的Environment(\.dismiss),直接为Sheet根视图绑定Escape键触发关闭操作:

import SwiftUI

struct ContentView: View {
    @State private var isSheetPresented = false
    
    var body: some View {
        VStack {
            Button("Show Sheet") {
                isSheetPresented.toggle()
            }
            .sheet(isPresented: $isSheetPresented) {
                SheetView()
            }
        }
    }
}

struct SheetView: View {
    @Environment(\.dismiss) private var dismiss
    
    var body: some View {
        VStack {
            Text("Hello")
            
            Button("Dismiss") {
                dismiss()
            }
        }
        .keyboardShortcut(.escape, action: dismiss)
    }
}

@main
struct testkeyboardApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

方案二:UIKit事件拦截(复杂场景适配)

如果方案一无效,可通过UIKit的事件处理直接拦截Escape键,确保在Catalyst环境下响应:

import SwiftUI
import UIKit

// 自定义视图容器,拦截键盘事件
struct EscapeHandlerView<Content: View>: UIViewControllerRepresentable {
    let content: Content
    let onEscape: () -> Void
    
    func makeUIViewController(context: Context) -> UIHostingController<Content> {
        let controller = UIHostingController(rootView: content)
        controller.becomeFirstResponder()
        return controller
    }
    
    func updateUIViewController(_ uiViewController: UIHostingController<Content>, context: Context) {
        uiViewController.rootView = content
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(onEscape: onEscape)
    }
    
    class Coordinator: NSObject, UIKeyInput {
        let onEscape: () -> Void
        
        init(onEscape: @escaping () -> Void) {
            self.onEscape = onEscape
            super.init()
        }
        
        var hasText: Bool { false }
        func insertText(_ text: String) {}
        func deleteBackward() {}
        
        override func pressesBegan(_ presses: Set<UIPress>, with event: UIPressesEvent?) {
            guard presses.first?.key.keyCode == .escape else {
                super.pressesBegan(presses, with: event)
                return
            }
            onEscape()
        }
    }
}

// 使用示例
struct SheetView: View {
    @Environment(\.dismiss) private var dismiss
    
    var body: some View {
        EscapeHandlerView(onEscape: dismiss) {
            VStack {
                Text("Hello")
                
                Button("Dismiss") {
                    dismiss()
                }
            }
        }
    }
}

方案一利用SwiftUI原生能力实现,代码简洁且适配多数场景;方案二通过UIKit底层事件处理,适合原生快捷键绑定失效的复杂交互场景。

内容的提问来源于stack exchange,提问作者John Cashew

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 04:01:29