如何在macOS上的SwiftUI中检测键盘事件?

如何在macOS上的SwiftUI中检测键盘事件?,swiftui,keydown,Swiftui,Keydown,如何在macOS上的SwiftUI视图中检测键盘事件 我希望能够使用按键来控制特定屏幕上的项目,但不清楚如何检测键盘事件,通常通过覆盖NSView中的按键向下(u-event:NSEvent)来检测键盘事件。目前为止,没有内置的本机SwiftUI API 这里只是一个可能的演示方法。使用Xcode 11.4/macOS 10.15.4进行测试 struct KeyEventHandling: NSViewRepresentable { class KeyView: NSView {

如何在macOS上的SwiftUI视图中检测键盘事件


我希望能够使用按键来控制特定屏幕上的项目,但不清楚如何检测键盘事件,通常通过覆盖NSView中的按键向下(u-event:NSEvent)来检测键盘事件。

目前为止,没有内置的本机SwiftUI API

这里只是一个可能的演示方法。使用Xcode 11.4/macOS 10.15.4进行测试

struct KeyEventHandling: NSViewRepresentable {
    class KeyView: NSView {
        override var acceptsFirstResponder: Bool { true }
        override func keyDown(with event: NSEvent) {
            super.keyDown(with: event)
            print(">> key \(event.charactersIgnoringModifiers ?? "")")
        }
    }

    func makeNSView(context: Context) -> NSView {
        let view = KeyView()
        DispatchQueue.main.async { // wait till next event cycle
            view.window?.makeFirstResponder(view)
        }
        return view
    }

    func updateNSView(_ nsView: NSView, context: Context) {
    }
}

struct TestKeyboardEventHandling: View {
    var body: some View {
        Text("Hello, World!")
            .background(KeyEventHandling())
    }
}
输出:


与Xcode 12捆绑在一起的SwiftUI中的新功能是修改了
命令,它允许我们使用声明键输入。然后需要某种方法将关键输入转发到子视图。下面是一个使用
主题
的解决方案,但由于它不是一个引用类型,因此无法使用
环境对象
传递它-这正是我们真正想要做的,因此我制作了一个小包装,符合
可观察对象
以及方便
主题
本身(通过
主题
转发)

使用一些额外的方便方法,我可以这样写:

。命令{
命令菜单(“输入”){
按键输入(.leftArrow)
按键输入(.rightArrow)
按键输入(.upArrow)
按键输入(.downArrow)
键输入(.space)
}
}
并将键输入转发到所有子视图,如下所示:

.environmentObject(keyInputSubject)
然后一个子视图,这里的
GameView
可以通过
onReceive
收听事件,如下所示:

struct游戏视图:视图{
@EnvironmentObject私有变量keyInputSubjectWrapper:keyInputSubjectWrapper
@StateObject var游戏:游戏
var body:一些观点{
HStack{
板
信息
}.onReceive(keyInputSubjectWrapper){
游戏.按键输入($0)
}
}
}
用于在
CommandMenu
builder中声明键的
keyinport
方法如下:

专用扩展ItsRainingPolygonsApp{
func keyInput(key:KeyEquivalent,修饰符:EventModifiers=.none)->一些视图{
键盘快捷方式(键,发件人:keyInputSubject,修饰符:修饰符)
}
}

完整代码
extension-KeyEquivalent:equalable{
公共静态函数==(左:Self,右:Self)->Bool{
lhs.character==rhs.character
}
}
公共类型别名KeyInputSubject=PassthroughSubject
公共最终类KeyInputSubjectWrapper:ObserveObject,Subject{
公共函数发送(值:输出){
objectWillChange.send(值)
}
公用函数发送(完成:订阅服务器。完成){
objectWillChange.send(完成:完成)
}
公用函数发送(订阅:订阅){
objectWillChange.send(订阅:订阅)
}
公共类型Alias ObjectWillChangePublisher=KeyInputSubject
public let objectWillChange:ObjectWillChangePublisher
public init(主题:ObjectWillChangePublisher=.init()){
objectWillChange=主题
}
}
//标记:发布者一致性
公共扩展键InputSubjectWrapper{
typealias输出=KeyInputSubject.Output
typealias Failure=KeyInputSubject.Failure
func receive(订阅服务器:S),其中S:subscriber,S.Failure==失败,S.Input==输出{
objectWillChange.receive(订户:订户)
}
}
@主要
结构ItsRainingPolygonsApp:App{
私有let keyInputSubject=KeyInputSubjectWrapper()
var body:一些场景{
窗口组{
#如果操作系统(macOS)
ContentView()
.frame(理想宽度:。无穷大,理想高度:。无穷大)
.onReceive(keyInputSubject){
打印(“按键:\($0)”)
}
.environmentObject(keyInputSubject)
#否则
ContentView()
#恩迪夫
}
.命令{
命令菜单(“输入”){
按键输入(.leftArrow)
按键输入(.rightArrow)
按键输入(.upArrow)
按键输入(.downArrow)
键输入(.space)
}
}
}
}
私有扩展ItsRainingPolygonsApp{
func keyInput(key:KeyEquivalent,修饰符:EventModifiers=.none)->一些视图{
键盘快捷方式(键,发件人:keyInputSubject,修饰符:修饰符)
}
}
公用func键盘快捷键(
_钥匙:钥匙等价物,
发送者:发送者,
修饰符:EventModifiers=.none,
@ViewBuilder标签:()->标签
)->一些视图,其中Label:View,Sender:Subject,Sender.Output==KeyEquivalent{
按钮(操作:{sender.send(key)},标签:label)
.键盘快捷方式(键、修改器:修改器)
}
公用func键盘快捷键(
_钥匙:钥匙等价物,
发送者:发送者,
修饰符:EventModifiers=.none
)->某些视图,其中Sender:Subject,Sender.Output==KeyEquivalent{
guard let nameFromKey=key.name else{
返回任意视图(EmptyView())
}
返回任意视图(键盘快捷键(键,发送者:发送者,修改者:修改者){
文本(“\(nameFromKey)”)
})
}
扩展键等效{
var lowerCaseName:字符串{
切换自身{
case.space:返回“space”
case.clear:返回“clear”
case.delete:返回“delete”
case.deleteForward:返回“delete forward”
case.downArrow:返回“downArrow”
case.end:返回“end”
case.escape:返回“escape”
案例.家:返回“家”
case.leftArrow:返回“leftArrow”
case.pageDown:返回“pageDown”
case.pageUp:返回“pageUp”
案例.返回:返回“返回”
case.rightArrow:返回“rightArrow”
case.space:返回“space”
case.tab:返回“tab”
case.upArrow:返回“upArrow”
默认值:返回零
}
}
变量名称:字符串{
小写字母