响应 SwiftUI 中的按键事件

Posted

技术标签:

【中文标题】响应 SwiftUI 中的按键事件【英文标题】:Respond to key press events in SwiftUI 【发布时间】:2020-04-25 17:57:53 【问题描述】:

我想响应按键,例如 macOS/OSX 上的 esc 键,以及在 iPad 上使用外接键盘时。我该怎么做?

我曾想过将 @available/#available 与 SwiftUI 的 onExitCommand 一起使用,这看起来很有希望,但不幸的是,这只适用于 macOS/OSX。如何响应 SwiftUI 中的按键而不仅仅是 macOS/OSX?

【问题讨论】:

目前还没有通用的方法。 UIKeyCommand 用于 iosNSEvent 用于 macOS。关注SwiftUI iOS - how to capture hardware key events 和 How to detect keyboard events in SwiftUI on macOS? 可能会有所帮助 @Asperi 很不幸。感谢这些链接,我会得到它的工作! 【参考方案1】:

更新:SwiftUI 2 现在有.keyboardShortcut(_:modifiers:)


旧答案:

感谢@Asperi 为我指明了正确的方向,我现在已经成功地完成了这项工作。

解决方案是使用UIKeyCommand。这是我所做的,但您可以根据自己的情况进行不同的调整。

我有一个名为AppState@EnvironmentObject,它可以帮助我设置委托,因此他们的键盘输入可以根据当前显示的视图而有所不同。

protocol KeyInput 
    
    func onKeyPress(_ key: String)



class KeyInputController<Content: View>: UIHostingController<Content> 
    
    private let state: AppState
    
    init(rootView: Content, state: AppState) 
        self.state = state
        super.init(rootView: rootView)
    
    @objc required dynamic init?(coder aDecoder: NSCoder) 
        fatalError("init(coder:) has not been implemented")
    
    
    
    override func becomeFirstResponder() -> Bool 
        true
    
    
    override var keyCommands: [UIKeyCommand]? 
        switch state.current 
        case .usingApp:
            return [
                UIKeyCommand(input: UIKeyCommand.inputEscape, modifierFlags: [], action: #selector(keyPressed(_:)))
            ]
            
        default:
            return nil
        
    
    
    @objc private func keyPressed(_ sender: UIKeyCommand) 
        guard let key = sender.input else  return 
        state.delegate?.onKeyPress(key)
    

AppState (@EnvironmentObject):

class AppState: ObservableObject 

    var delegate: KeyInput?
    /* ... */

场景委托看起来像:

let stateObject = AppState()
let contentView = ContentView()
    .environmentObject(stateObject)

// Use a UIHostingController as window root view controller.
if let windowScene = scene as? UIWindowScene 
    let window = UIWindow(windowScene: windowScene)
    window.rootViewController = KeyInputController(rootView: contentView, state: stateObject)

    /* ... */

这使得现在根据按下的键添加功能变得非常容易。

符合KeyInput,例如:

struct ContentView: View, KeyInput 

    /* ... */

    var body: some View 
        Text("Hello world!")
            .onAppear 
                self.state.delegate = self
            
    
    
    func onKeyPress(_ key: String) 
        print(key)
        guard key == UIKeyCommand.inputEscape else  return 
        // esc key was pressed
        /* ... */
    

【讨论】:

【参考方案2】:

在 macOS 和 tvOS 上,有一个 onExitCommand(perform:) 修饰符用于查看。 来自 Apple 的documentation:

设置在视图获得焦点时响应接收到退出命令而触发的操作。

用户通过按 tvOS 上的菜单按钮或 macOS 上的退出键来生成退出命令。

例如:

struct ContentView: View 
    
    var body: some View 
        VStack 
            
            TextField("Top", text: .constant(""))
                .onExitCommand(perform: 
                    print("Exit from top text field")
                )
            
            TextField("Bottom", text: .constant(""))
                .onExitCommand(perform: 
                    print("Exit from bottom text field")
                )
        
        .padding()
    
    

【讨论】:

上述示例在 macOS 上运行良好,但是,如果 TextField 位于 ScrollView 而不是 VStack 中,则不会执行 TextField 的 onExitCommand 操作。向 ScrollView 添加 onExitCommand 修饰符将捕获转义,但不会直接告诉您哪个(如果有)有焦点。【参考方案3】:

更简单的解决方案是 .onExitCommand 修饰符。 正如@Chuck H 评论的那样,如果文本字段在 ScrollView 内或在我的情况下为 Section 内,则这不起作用。 但我发现,只需将 TextField 及其 .onExitCommand 嵌入到 HStack 或 VStack 中,.onExitCommand 就可以正常工作。

                    HStack  // This HStack is for .onExitCommand to work
                        TextField("Nombre del proyecto", text: $texto)
                        .onExitCommand(perform: 
                            print("Cancelando edición....")
                        )
                        .textFieldStyle(PlainTextFieldStyle())
                        .lineLimit(1)
                    

【讨论】:

非常感谢!!!节省了我的时间,您的解决方案也适用于List!你摇滚! 只是想知道有没有办法检测 TextField 何时失去焦点,不幸的是 @FocusedState didSet 没有检测到它 .onChange(of: isNewFolderInFocus) newValue in 似乎是检测所有值变化的方法。检测用户何时在文本字段外点击很有用

以上是关于响应 SwiftUI 中的按键事件的主要内容,如果未能解决你的问题,请参考以下文章

linux下获取按键响应事件

JavaScript onkeydown事件入门实例(键盘某个按键被按下)

如何使用 Marionette 创建全局按键事件侦听器

Android怎样监听蓝牙耳机的按键事件

quick-cocos2d-x游戏开发12——硬件按键事件

c ++ ubuntu检测实时按键事件