SwiftUI iOS - 如何捕捉硬件按键事件

SwiftUI iOS - how to capture hardware key events

我是 iOS 开发的新手。按照教程,我使用 SwiftUI 创建了一个简单的计算器。

我的 iPad 上连接了一个键盘,我希望能够使用键盘输入值。

如何在 SwiftUI 应用程序(没有文本字段)中捕获和处理硬件键盘事件? 我已尝试在 SceneDelegate (UIResponder) 上使用 keyCommand,如此处所示,但这对我不起作用。只要我在 iPad 上按任意键,我就会在 XCode 跟踪视图中看到“与守护进程的连接无效”。

 class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    var window: UIWindow?

    override var canBecomeFirstResponder: Bool {
        return true;
    }
    override var keyCommands: [UIKeyCommand]? {
        return [
            UIKeyCommand(input: "a", modifierFlags: [], action: #selector(test)),
            UIKeyCommand(input: UIKeyCommand.inputLeftArrow, modifierFlags: [], action: #selector(test))
        ]
    }

    @objc func test(_ sender: UIKeyCommand) {
        print("test was pressed")
    }

谢谢

它需要改写托管视图控制器,一切正常。使用 Xcode 11.2 / iOS 13.2

测试

这是示例代码

class KeyTestController<Content>: UIHostingController<Content> where Content: View {

    override func becomeFirstResponder() -> Bool {
        true
    }
    
    override var keyCommands: [UIKeyCommand]? {
        return [
            UIKeyCommand(input: "1", modifierFlags: [], action: #selector(test)),
            UIKeyCommand(input: "0", modifierFlags: [], action: #selector(test)),
            UIKeyCommand(input: UIKeyCommand.inputLeftArrow, modifierFlags: [], action: #selector(test))
        ]
    }

    @objc func test(_ sender: UIKeyCommand) {
        print(">>> test was pressed")
    }

}

以及下方 SceneDelegate 的某处

window.rootViewController = KeyTestController(rootView: contentView)

backup

如果您使用的是 SwiftUI 生命周期,@Asperi 还展示了如何在此 post - Hosting Controller When Using iOS 14 @main.

中访问 rootViewController

总而言之,HostingWindowFinder 追踪 rootViewController 的可变版本并提供对它的访问。

struct ContentView: View {

    var body: some View {
      Text("Demo Root Controller access")
        .withHostingWindow { window in
            window?.rootViewController = KeyController(rootView: ContentView())
        }
    }
}

extension View {
    func withHostingWindow(_ callback: @escaping (UIWindow?) -> Void) -> some View {
        self.background(HostingWindowFinder(callback: callback))
    }
}

struct HostingWindowFinder: UIViewRepresentable {
    var callback: (UIWindow?) -> ()

    func makeUIView(context: Context) -> UIView {
        let view = UIView()
        DispatchQueue.main.async { [weak view] in
            self.callback(view?.window)
        }
        return view
    }

    func updateUIView(_ uiView: UIView, context: Context) {
    }
}

还有

如果您只想监视原始键盘输入,请使用 pressesBegan(...) - WWDC 2019 Talk

class KeyController<Content>: UIHostingController<Content> where Content: View {

    override func pressesBegan(_ presses: Set<UIPress>, with event: UIPressesEvent?) {
        for press in presses {
            guard let key = press.key else { continue }
            print(key)
        }
    }
}

非常感谢@Asperi! ❤️