iPadOS 15 beta 中使用 UIKeyCommand 的键盘快捷键

Keyboard shortcuts with UIKeyCommand in iPadOS 15 beta

出于某种原因,我无法在 iPadOS 15 (beta 5) 中使用硬件键盘快捷键。它们适用于大多数键,但不适用于箭头键和 Tab 键。

相同的代码在 iPadOS 14.5 模拟器的 Xcode 13(测试版 4)和 运行 中编译时似乎运行良好,但在使用相同的 Xcode 构建时拒绝运行但在 iPadOS 15 sim 上。我已经在装有 iPadOS 15 beta 的实际设备上尝试了最多 5 个,结果相同。

这是一个最小的例子:

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        addKeyCommand(UIKeyCommand(title: "UP", action: #selector(handle(key:)), input: UIKeyCommand.inputUpArrow, modifierFlags: []))
        addKeyCommand(UIKeyCommand(title: "DOWN", action: #selector(handle(key:)), input: UIKeyCommand.inputDownArrow, modifierFlags: []))
        addKeyCommand(UIKeyCommand(title: "TAB", action: #selector(handle(key:)), input: "\t", modifierFlags: []))
    }

    @objc func handle(key: UIKeyCommand?) {
        NSLog("Intercepted key: \(key?.title ?? "Unknown")")
    }
}

我还没有找到任何相关报告或公开雷达,所以我怀疑我可能在这里遗漏了一些东西。如果应该报告,我应该在哪里报告这样的错误?

谢谢。

貌似有一个新的UIKeyCommand属性wantsPriorityOverSystemBehaviorsome需要设置为true键——就像我在问题中提到的那些:https://developer.apple.com/documentation/uikit/uikeycommand/3780513-wantspriorityoversystembehavior

解决方案确实是使用wantsPriorityOverSystemBehavior。但是,由于您使用的是 UIResponder 的子类,而不是为已知键添加键命令,因此您可以考虑使用内置方法。它比单独添加一个更有效,而且就模式而言更干净。

class ViewController: UIViewController {

    /// - SeeAlso: UIViewController.viewDidLoad()
    override func viewDidLoad() {
        super.viewDidLoad()
        // do other things, nothing connected with UIKeyCommand
    }

    /// - SeeAlso: UIResponder.keyCommands
    override var keyCommands: [UIKeyCommand]? {
        let commands = [
            UIKeyCommand(input: UIKeyCommand.inputUpArrow, modifierFlags: [], action: #selector(actionUp)),
            UIKeyCommand(input: UIKeyCommand.inputDownArrow, modifierFlags: [], action: #selector(actionDown)),
            UIKeyCommand(input: UIKeyCommand.inputLeftArrow, modifierFlags: [], action: #selector(actionLeft)),
            UIKeyCommand(input: UIKeyCommand.inputRightArrow, modifierFlags: [], action: #selector(actionRight))
        ]
        // if your target is iOS 15+ only, you can remove `if` and always apply this rule
        if #available(iOS 15, *) { 
            commands.forEach { [=10=].wantsPriorityOverSystemBehavior = true }
        }
        return commands
    }
}

private extension ViewController {
   
    @objc func actionUp() { print("up") }
    @objc func actionDown() { print("down") }
    @objc func actionLeft() { print("left") }
    @objc func actionRight() { print("right") }
}

很抱歉,这是一个答案,但评论不允许好的代码语法 :-) 希望你不介意我的补充,但也许有人不知道 keyCommands 和发现它在这种情况下很有用。至少我提供了一个示例,说明如何使用创建者@AlexStaravoitau 已经写过的 API 的这个新部分。


Apple 声称:

Prior to iOS 15, the system delivered keyboard events to your key command objects first, and then to the text input or focus systems. If your app links against iOS 14 SDK or earlier, your app retains that behavior, even when running on iOS 15 or later.

这是一个非常重要的小细节,值得牢记。我误读了它并认为如果你支持 iOS 14 或更低它会以旧方式工作。但这是我错误的理解,它完全是关于链接的(例如,如果您使用 Xcode 12 或更早版本构建并且不在库中添加额外的 iOS)。 因此,即使我的应用程序仍然支持 iOS 12+,关键命令不再适用于 Xcode 13 到 iOS 15 的新版本。只有在我调整了上述内容后它才开始工作旗帜.