不清楚您是否有一个NSTextField 作为您的菜单视图,但如果您使用一个,那么很容易为该文本字段设置一个委托,该委托可以在用户键入时获取该字段的当前内容(这会处理他们用箭头键向后移动,然后删除字符,使用删除键等)。您的委托实现了适当的委托方法并在每次文本更改时被调用:
extension CustomMenuItemViewController: NSTextFieldDelegate {
func controlTextDidChange( _ obj: Notification) {
if let postingObject = obj.object as? NSTextField {
let text = postingObject.stringValue
print("the text is now: \(text)")
}
}
}
为了确认这按预期工作,我在 xib 文件中为自定义菜单项视图(标签 + 编辑字段)创建了 ViewController 类,然后动态构建了一个简单的测试菜单,其中包含一个具有自定义视图的菜单项控制器的视图并将其添加到我的应用程序委托中的菜单栏:
func installCustomMenuItem() {
let menuBarItem = NSMenuItem(title: "Test", action: nil, keyEquivalent: "")
let menu = NSMenu(title: "TestMenu" )
let subMenuBarItem = NSMenuItem(title: "Custom View", action: nil, keyEquivalent: "")
subMenuBarItem.view = menuItemVC.view
menu.addItem(subMenuBarItem)
menuBarItem.submenu = menu
NSApp.mainMenu?.addItem(menuBarItem)
}
在我输入“你好”后看起来像这样:
您可以从控制台为每个键入的字符调用我的处理程序:
the text is now: H
the text is now: He
the text is now: Hel
the text is now: Hell
the text is now: Hello
您的情况可能有点不同,但这种方法似乎很干净,可能对您有用。如果由于某种原因无法使用,请添加澄清评论,我们会看看我们是否无法让它为您服务。
加法:
我突然想到你可能不希望使用NSTextField,所以我很好奇使用自定义视图是否同样容易做到这一点并且相对容易。
创建NSView的子类:
class CustomMenuView: NSView {
override var acceptsFirstResponder: Bool {
return true
}
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
// Drawing code here.
}
override func keyDown(with event: NSEvent) {
print("key down with character: \(String(describing: event.characters)) " )
}
}
在自定义视图控制器中将根视图的类设置为这种类型,然后像以前一样完成所有操作 - 视图控制器加载到 applicationDidFinishLaunching 和菜单构建和视图控制器的视图(现在是 @987654332 @) 设置为 menuBarItem.view。
就是这样。现在,当下拉菜单时,您会为每个键按下调用您的 keyDown 方法。
key down with character: Optional("H")
key down with character: Optional("e")
key down with character: Optional("l")
key down with character: Optional("l")
key down with character: Optional("o")
key down with character: Optional(" ")
key down with character: Optional("T")
key down with character: Optional("h")
key down with character: Optional("i")
key down with character: Optional("s")
key down with character: Optional(" ")
key down with character: Optional("i")
key down with character: Optional("s")
key down with character: Optional(" ")
key down with character: Optional("c")
key down with character: Optional("o")
key down with character: Optional("o")
key down with character: Optional("l")
:)
现在您的自定义视图(如果您喜欢,还可以包含子视图)可以自己绘制等等。
在没有 ViewController 的情况下添加请求的示例:
// Simple swift playground test
// The pop-up menu will show up onscreen in the playground at a fixed location.
// Click in the popup and then all key commands will be logged.
// The ViewController in my example above may be taking care of putting the custom view in the responder chain, or the fact that it's in a menubar and being invoked via a MenuItem might be.
// I'd suggest trying it in the actual environment rather than in a playground. In my test app you click the menu name in the menubar to drop down the menu and it is added to the responder chain and works as expected without having to click in the menu first to get the events flowing.
// There is no reason you need to be hooking events either with carbon events or the newer format. If you're in the responder chain of and implement the necessary, method then you'll get the key events you're looking for.
import AppKit
class CustomMenuView: NSView {
override var acceptsFirstResponder: Bool {
return true
}
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
// Drawing code here.
}
override func keyDown(with event: NSEvent) {
print("key down with character: \(String(describing: event.characters)) " )
}
}
func installCustomMenuItem() -> NSMenu {
// let menuBarItem = NSMenuItem(title: "Test", action: nil, keyEquivalent: "")
let resultMenu = NSMenu(title: "TestMenu" )
let subMenuBarItem = NSMenuItem(title: "Custom View", action: nil, keyEquivalent: "")
subMenuBarItem.view = CustomMenuView(frame: NSRect(x: 0, y: 0, width: 40, height: 44))
resultMenu.addItem(subMenuBarItem)
// menuBarItem.submenu = menu
return resultMenu
}
var menu = installCustomMenuItem()
menu.popUp(positioning: nil, at: NSPoint(x:600,y:400), in: nil)