我有一个多窗口应用程序,因此我使用
focusedSceneObject
将交互定向到当前窗口。然后,我在视图中看到一个 @FocusedObject
,这在视图出现时似乎是正确的。但是,视图需要查看键盘事件,因此我有一个视图利用 NSViewRepresentable
提供一种访问 SwiftUI 中的事件的方法。问题是当视图响应事件时,@FocusedObject
变量为零。
这是
NSViewRepresentable
视图:
import SwiftUI
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)
}
}
该应用程序具有此类代码(为了清楚起见,删除了一些细节):
extension FocusedValues {
struct DocumentFocusedValues: FocusedValueKey {
typealias Value = MyDocument
}
var document: MyDocument? {
get { self[DocumentFocusedValues.self] }
set { self[DocumentFocusedValues.self] = newValue }
}
}
@main
struct MyApp: App {
var body: some Scene {
DocumentGroup {
MyDocument()
} editor: { file in
DocumentView(document: file.document)
.focusedSceneObject(file.document as MyDocument)
}
}
}
视图的简化版本:
struct DocumentView: View {
@FocusedObject var focusedDocument: MyDocument?
var body: some View {
ZStack {
NavigationSplitView {
// The list view
}
detail: {
// The detail view
}
.focusable()
KeyAwareView { event in
switch event.type {
case .keyDown:
let notification = Notification(name: .keyDown, object: focusedDocument, userInfo: [KeyKeyCode: Int(event.keyCode)])
NotificationCenter.default.post(name: .keyboardEvent, object: focusedDocument, userInfo: [KeyNotification: notification])
case .keyUp:
// Code for key up event
case .flagsChanged:
// Code for modifiers changed event
default:
break
}
}
}
}
}
当我遇到
keyDown
的情况时,focusedDocument
为零。我做错了什么? (顺便说一句,我尝试过删除 .focusable()
语句,或将其附加到层次结构的不同部分,但这没有什么区别。)
问题是被调用的块可以访问上下文,但是
@FocusedObject
没有在那里设置,因此工作必须在视图结构内完成。
执行此操作的一种简单方法是在
KeyAwareView
变量的 NSEvent?
中创建绑定,然后修改视图以添加将事件传递给绑定变量的协调器。然后工作在文档视图的 onChangeOf
修饰符中完成。
这是新版本的
KeyAwareView
:
protocol KeyEventHandler {
func handleEvent(event: NSEvent)
}
struct KeyAwareView: NSViewRepresentable {
class Coordinator: NSObject, KeyEventHandler {
var parent: KeyAwareView
init(_ parent: KeyAwareView) {
self.parent = parent
}
func handleEvent(event: NSEvent) {
parent.event = event
}
}
@Binding var event: NSEvent?
func makeNSView(context: Context) -> NSView {
let view = KeyView()
view.handler = context.coordinator
DispatchQueue.main.async {
view.window?.makeFirstResponder(view)
}
return view
}
func updateNSView(_ nsView: NSView, context: Context) {}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
}
private class KeyView: NSView {
var handler: KeyEventHandler?
override var acceptsFirstResponder: Bool { true }
override func keyDown(with event: NSEvent) {
handler?.handleEvent(event: event)
}
override func keyUp(with event: NSEvent) {
handler?.handleEvent(event: event)
}
override func flagsChanged(with event: NSEvent) {
handler?.handleEvent(event: event)
}
}