SwiftUI 如何在 macOS 的 keyUp 事件中改变状态?

4

当从窗口检测到keyUp事件时,我想更改状态值。然而,这似乎对我不起作用。下面是我的代码:

/// Window
class Window: NSWindow {
  override func keyUp(with event: NSEvent) {
    (contentView as! NSHostingView<ContentView>).rootView.keyPressed(with: event)
  }
}

/// ContentView
struct ContentView {
  @State var index: Int = 0
  var body: some View { ... }

  func keyPressed(with event: NSEvent) {
    self.index = self.index + 1
  }
}

我使用了调试器来测试它,显然,keyPressed已经被成功地调用了,但是索引没有正确设置。有人知道为什么吗?或者在SwiftUI中监听macOS键盘的正确策略是什么?


你好,感谢留言。我之前确实尝试过这个方法,但并不完全符合我的需求。当我使用他的方法时,会出现阻塞的情况。我正在尝试实现类似于这里提问的问题:https://dev59.com/4VIH5IYBdhLWcg3wZtbI - WatashiJ
1个回答

8
这是可能的方法演示。已在Xcode 11.4 / macOS 10.15.4上测试。
这个想法是通过注入发布者到环境值,将自定义NSWindow生成的键事件与SwiftUI View连接起来。这使得可以在任何视图层次结构水平上监听和处理事件。
以下是完整模块(AppDelegate.swift)代码。还请参阅代码中有用的注释。
import Cocoa
import SwiftUI
import Combine

// Environment key to hold even publisher
struct WindowEventPublisherKey: EnvironmentKey {
    static let defaultValue: AnyPublisher<NSEvent, Never> = 
        Just(NSEvent()).eraseToAnyPublisher() // just default stub
}


// Environment value for keyPublisher access
extension EnvironmentValues {
    var keyPublisher: AnyPublisher<NSEvent, Never> {
        get { self[WindowEventPublisherKey.self] }
        set { self[WindowEventPublisherKey.self] = newValue }
    }
}

// Custom window holding publisher and sending events to it. In general 
// it can be any event, but for originated question we limit to keyUp events
class Window: NSWindow {
    private let publisher = PassthroughSubject<NSEvent, Never>() // private

    var keyEventPublisher: AnyPublisher<NSEvent, Never> { // public
        publisher.eraseToAnyPublisher()
    }

    override func keyUp(with event: NSEvent) {
        publisher.send(event)
    }
}

// Root demo view
struct DemoKeyPressedView: View {
    @Environment(\.keyPublisher) var keyPublisher // << access to publisher

    @State private var index: Int = 0
    var body: some View {
        Text("Demo \(index)")
            .onReceive(keyPublisher) { event in // << listen to events
                self.keyPressed(with: event)
            }
    }
    
    func keyPressed(with event: NSEvent) {
        self.index += 1
    }
}

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

    var window: Window!


    func applicationDidFinishLaunching(_ aNotification: Notification) {

        // Create the custom window
        window = Window(
            contentRect: NSRect(x: 0, y: 0, width: 480, height: 300),
            styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
            backing: .buffered, defer: false)
        window.center()
        window.setFrameAutosaveName("Main Window")

        // Create the SwiftUI view that provides the window contents.
        let contentView = DemoKeyPressedView()
            .frame(minWidth: 400, maxWidth: .infinity, maxHeight: .infinity)
            .environment(\.keyPublisher, window.keyEventPublisher) // inject publisher

        window.contentView = NSHostingView(rootView: contentView)
        window.makeKeyAndOrderFront(nil)
    }

    func applicationWillTerminate(_ aNotification: Notification) {
        // Insert code here to tear down your application
    }

    func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
        return true
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接