SwiftUI如何在keyUp macOS事件上更改状态? [英] SwiftUI how to change state on keyUp macOS event?

查看:113
本文介绍了SwiftUI如何在keyUp macOS事件上更改状态?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试从窗口检测到keyUp时更改状态值.但是,它似乎对我不起作用.这是我的代码

I am trying to change a state value when keyUp is detected from window. However, it doesn't seem to work for me. Here is my code

/// 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中聆听键盘的正确策略是什么?

I used debugger to test it, apparently, the keyPressed is called successfully, but the index is not set correctly. Anyone knows why? Or what is the correct strategy to listen to keyboard in SwiftUI for macOS?

推荐答案

以下是可能方法的演示.使用Xcode 11.4/macOS 10.15.4进行了测试

Here is a demo of possible approach. Tested with Xcode 11.4 / macOS 10.15.4

这个想法是通过SwiftUI View通过注入的发布者将定制的NSWindow加入到环境值中,从而生成自定义的NSWindow.这样就可以在任何级别的视图层次上侦听/处理事件.

The idea is to join custom NSWindow, generating key events, with SwiftUI View via injected publisher to environment values. This gives possibility to listen/handle events at any level of view hierarchy.

下面是完整的模块(AppDelegate.swift)代码.另请参见代码中的有用注释.

Below is full module (AppDelegate.swift) code. Also see useful comments in code.

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
    }
}

这篇关于SwiftUI如何在keyUp macOS事件上更改状态?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆