I have an application that needs to respond to key down/up and modifier changed events. This is the code for the view that receives the keyboard events:
struct KeyAwareView: NSViewRepresentable {
let onEvent: (NSEvent) -> Void
func makeNSView(context: Context) -> NSView {
let view = KeyView()
view.onEvent = onEvent
DispatchQueue.main.async {
view.window?.makeFirstResponder(view)
}
return view
}
func updateNSView(_ nsView: NSView, context: Context) {}
}
private class KeyView: NSView {
var onEvent: (NSEvent) -> Void = { _ in }
override var acceptsFirstResponder: Bool { true }
override func keyDown(with event: NSEvent) {
onEvent(event)
}
override func keyUp(with event: NSEvent) {
onEvent(event)
}
override func flagsChanged(with event: NSEvent) {
onEvent(event)
}
}
I then have this view layered in a ZStack
:
KeyAwareView(onEvent: { event in
switch event.type {
case .keyDown:
KeyboardEventResponder.shared.handleKeyDown(keyCode: Int(event.keyCode))
case .keyUp:
KeyboardEventResponder.shared.handleKeyUp(keyCode: Int(event.keyCode))
case .flagsChanged:
KeyboardEventResponder.shared.updateModifiers(newModifiers: event.modifierFlags)
default:
break
}
})
The main window is a master-detail view which looks like this (some detail removed):
ZStack() {
NavigationView {
LayoutList(layoutsList: $document.layouts, selectedLayout: $selectedLayout)
if selectedLayout != nil {
let index = document.layouts.firstIndex(of: selectedLayout!)!
if let layout = document.loadLayout(atIndex: index) {
LayoutEditor(layoutData: $document.layouts[index])
.environmentObject(keyboardStatus)
}
}
}
KeyAwareView { event in
switch event.type {
case .keyDown:
KeyboardEventResponder.shared.handleKeyDown(keyCode: Int(event.keyCode))
case .keyUp:
KeyboardEventResponder.shared.handleKeyUp(keyCode: Int(event.keyCode))
case .flagsChanged:
KeyboardEventResponder.shared.updateModifiers(newModifiers: event.modifierFlags)
default:
break
}
}
}
This all works properly when the first layout is selected. But then when you click on another layout, the KeyAwareView
doesn't receive any events until I click somewhere in the window apart from selecting another layout in the list.
What can I do to make the view receive events? I tried adding
DispatchQueue.main.async {
nsView.window?.makeFirstResponder(nsView)
}
to the updateNSView
method, but that made no difference.