ios - SwiftUI 如何更改 keyUp macOS 事件的状态?
问题描述
当从窗口中检测到 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调用成功了,但是索引设置不正确。有谁知道为什么?或者在 macOS 的 SwiftUI 中听键盘的正确策略是什么?
解决方案
这是一个可能的方法的演示。使用 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
}
}
推荐阅读
- java - 如果我已经有 changelog.xml,如何正确创建外部 liquibase-changeSet-xml?
- c++ - 为什么 C++ 输出直到我按下回车后才到达套接字?
- javascript - 警告:正在导入 Three.js 的多个实例
- python - 按范围内的日期获取计数(MongoDB 聚合管道)
- python - 为什么 selenium 运行 webassembly 驱动的应用程序比手动测试慢 5 倍?
- python - 尝试打印属性值...代码需要改进
- kotlin - 是否可以在 when 条件下使用 Kotlin 的捕获?
- mysql - MySQL比较没有ID的行
- ruby-on-rails - 具有主要类别的类别(Ruby on Rails)
- javascript - 折叠导航栏未从引导程序打开备份