0

我正在为我的应用程序添加键盘快捷键。有一个视图控制器呈现另一个控制器:

class ViewController: UIViewController {

    override var canBecomeFirstResponder: Bool { true }

    override func viewDidLoad() {
        super.viewDidLoad()

        addKeyCommand(UIKeyCommand(
            input: "M",
            modifierFlags: .command,
            action: #selector(ViewController.handleKeyCommand),
            discoverabilityTitle: "Command from the container view"
        ))
    }

    @objc func handleKeyCommand() {
        present(ModalViewController(), animated: true)
    }

    override func canPerformAction(
        _ action: Selector, withSender sender: Any?
    ) -> Bool {
        if action == #selector(ViewController.handleKeyCommand) {
            return isFirstResponder
        }

        return super.canPerformAction(action, withSender: sender)
    }
}

class ModalViewController: UIViewController {

    override var canBecomeFirstResponder: Bool { true }

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .white
        addKeyCommand(UIKeyCommand(
            input: "D",
            modifierFlags: .command,
            action: #selector(ModalViewController.handleKeyCommand),
            discoverabilityTitle: "Command from the modal view"
        ))

        if !becomeFirstResponder() {
            print("⚠️ modal did not become first responder")
        }
    }

    @objc func handleKeyCommand() {
        dismiss(animated: true)
    }
}

两者都定义了快捷方式。当模态视图控制器被呈现时,Discoverability 弹出窗口包括用于呈现和呈现的视图控制器的快捷方式。直观地说,应该只包含模态视图控制器的快捷方式(我们不应该能够与呈现的视图控制器交互,对吧?)

我可以通过覆盖呈现视图控制器的keyCommands属性来解决这个问题,但这是个好主意吗?

一般来说,这种行为背后的原因是什么?这是错误还是功能?


更新:添加canPerformAction(_:sender:)到呈现视图控制器(如@buzzert所建议),但问题仍然存在。

4

2 回答 2

1

发生这种情况是因为呈现视图控制器 ( ViewController) 是您在响应ModalViewControllernextResponder链中的 。

这是因为操作系统需要某种方式从当前显示在屏幕上的视图控制器一直跟踪到应用程序。

如果您的呈现视图控制器只有在它是第一响应者时才有意义的命令,解决这个问题的最简单方法是简单地覆盖canPerformAction(_:)on ViewControllerfalse如果它不是第一响应者则返回。

例如,

    override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
        if self.isFirstResponder {
            return super.canPerformAction(action, withSender: sender)
        } else {
            return false
        }
    }

否则,如果您想对响应程序链中的 进行更多控制nextResponder,您还可以覆盖nextRespondergetter 以“跳过”您的呈现视图控制器。虽然不推荐这样做,但可以作为其工作原理的说明。

于 2020-06-24T21:47:57.713 回答
0

参考@buzzert,我在根视图控制器中使用此代码,它似乎工作正常。

override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
    if sender is UIKeyCommand{ /* keyboard event */
        if self.presentedViewController != nil{ /* modal now */
            return false
        }
    }
    
    return super.canPerformAction(action, withSender: sender)
}
于 2022-01-24T10:19:59.657 回答