Непонятно, есть ли у вас 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)")
}
}
}
Просто чтобы подтвердить, что это работает должным образом, я создал класс ViewController для настраиваемых представлений элементов меню (поле label + edit) в файле xib, а затем динамически создал простое тестовое меню с одним элементом меню, имеющим настраиваемое представление. контроллер и добавил его в меню в моем делегате приложения:
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
, построение меню и просмотр представления контроллера (который теперь * 1031) *) устанавливается как меню BarItem.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)