Cocoa 箭头键的应用程序响应程序链
我的窗口中有一个Cocoa 箭头键的应用程序响应程序链,cocoa,first-responder,nsmenuitem,nsapplication,nsresponder,Cocoa,First Responder,Nsmenuitem,Nsapplication,Nsresponder,我的窗口中有一个NSTextField,有4个菜单项和对应的键←↑→↓. 当选择文本字段并按下箭头键时,我希望光标在文本字段中移动,但会执行相应的菜单项操作 因此,在响应者链中必须存在一个问题。为了找出我在这篇文章中提到的问题 按键(热键)的事件流在会话中显示如下: 因此,我用一个菜单项检查了调用堆栈,该菜单项具有keyEquivalent=K(只是任何普通键)和一个菜单项具有keyEquivalent=→(右箭头键) 第一:K键事件调用堆栈;第二:右箭头键事件调用堆栈 因此,当按下
NSTextField
,有4个菜单项和对应的键←↑→↓.
当选择文本字段并按下箭头键时,我希望光标在文本字段中移动,但会执行相应的菜单项操作
因此,在响应者链中必须存在一个问题。为了找出我在这篇文章中提到的问题
按键(热键)的事件流在会话中显示如下:
因此,我用一个菜单项检查了调用堆栈,该菜单项具有
keyEquivalent=K
(只是任何普通键)和一个菜单项具有keyEquivalent=→代码>(右箭头键)
第一:K键事件调用堆栈;第二:右箭头键事件调用堆栈
因此,当按下箭头键时,事件将直接发送到主菜单。performKeyEquivalent
,但它实际上应该发送到键窗口
,对吗
这是为什么?我如何修复此行为,使我的NSTextField
在主菜单
执行之前接收箭头键事件?关于调用堆栈差异的有趣观察。由于箭头键在导航中起着最重要的作用,它们的处理方式可能与其他键不同,正如您在线程中看到的。同样,AppKit在99.9%的情况下会处理好幕后的一切,让您的生活更轻松,这也是其中之一
当文本字段具有焦点时,按k键可以看到行为的实际差异。与箭头不同,菜单项的等价键不会被触发,输入直接进入控件
对于您的情况,您可以使用协议覆盖启用或禁用特定菜单项的默认操作。AFAIK这可以进入链中的任何响应程序,例如视图控制器、窗口或应用程序。因此,当窗口的第一个响应程序是textfield或使用这些事件正常运行的任何其他控件时,可以在单个位置启用/禁用菜单项
extension ViewController: NSMenuItemValidation {
func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
// Filter menu item by it's assigned action, just as an exampe.
if menuItem.action != #selector(ViewController.menuActionLeftArrowKey(_:)) { return true }
Swift.print("Validating menu item:", menuItem)
// Disable the menu item if first responder is text view.
let isTextView = self.view.window?.firstResponder is NSTextView
return !isTextView
}
}
这将在显示菜单以更新项目状态之前被调用,在调用菜单项键以检查操作是否需要发送之前被调用,并且在其他情况下,当AppKit需要检查项目状态时可能会被调用——我想不出有什么
如上所述,第一响应者检查是针对NSTextView
而不是NSTextField
,原因。这是我选择的解决方案,它是由@Willeke的评论得出的
我已经创建了NSWindow
的子类,并覆盖了keyDown(with:)
方法。我的应用程序中的每个窗口(当前为2个)都将这个新的导航窗口子类化,以便您可以在每个窗口中使用箭头键
class NavigationWindow: NSWindow {
override func keyDown(with event: NSEvent) {
if event.keyCode == 123 || event.keyCode == 126 || event.specialKey == NSEvent.SpecialKey.pageUp {
print("navigate back")
} else if event.keyCode == 124 || event.keyCode == 125 || event.specialKey == NSEvent.SpecialKey.pageDown {
print("navigate forward")
} else {
super.keyDown(with: event)
}
}
}
此实现注册所有四个箭头键以及页面上下键以进行导航
这些是关键代码
123
:右箭头
124
:左箭头
125
:向下箭头
126
:向上箭头
看。@Willeke我已经看过了,但我仍然不明白为什么会有这种行为。在图中显示,如果它是一个键等价物,那么它将被发送到键窗口视图层次结构。这种情况发生在P快捷方式上,但不发生在箭头上。根据图表,箭头应该被发送给第一响应者,但这并没有发生。可能箭头键最初被视为等同于键,而没有命令键修饰符的P则不是。调用堆栈不是事件所采用的路由。请参阅。注意:现在视图控制器也在响应器链中。如果不处理按键,请调用super.keyDown(with:event)
。非常感谢您的回答!这是一个有趣的解决方案。但不幸的是,我无法在我的应用程序中应用此功能,因为用户需要能够在编辑时选择菜单项…很好!您可能需要使用关键常量,如kVK_LeftArrow、kVK_RightArrow等。大多数都可以看到。