Удалить "Начать диктовку" и "Специальные символы" из меню
Чтобы включить копирование и вставку в моем приложении Cocoa, я добавил два новых пункта меню (копирование и вставка) в меню и перетянул селектор из каждого элемента в первый респондент (копирование и вставка). Однако под пунктами меню "Копировать" и "Вставить" отображаются два дополнительных пункта: "Начать диктовку" и "Специальные символы".
Я не смог понять, почему они появляются или как я их удаляю.
Оптимально, я даже не хочу, чтобы пункты меню копирования и вставки были видны. Я просто хочу, чтобы пользователь моего приложения мог вставить материал (например, из электронного письма, текстового документа и т. Д.) В текстовое поле на одной из форм в моем приложении.
7 ответов
Как упоминалось в Mac OS X Internals: системный подход и Qt Mac (Re) перемещают действие "Специальные символы..." в меню "Правка", вы можете сделать что-то подобное в main() перед загрузкой пера (но это не так). поддерживаемый API):
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"NSDisabledDictationMenuItem"];
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"NSDisabledCharacterPaletteMenuItem"];
Вот код, который я использую в своем приложении для удаления этих автоматически добавленных записей в меню "Правка":
- (void) applicationDidFinishLaunching: (NSNotification*)aNotification
{
NSMenu* edit = [[[[NSApplication sharedApplication] mainMenu] itemWithTitle: @"Edit"] submenu];
if ([[edit itemAtIndex: [edit numberOfItems] - 1] action] == NSSelectorFromString(@"orderFrontCharacterPalette:"))
[edit removeItemAtIndex: [edit numberOfItems] - 1];
if ([[edit itemAtIndex: [edit numberOfItems] - 1] action] == NSSelectorFromString(@"startDictation:"))
[edit removeItemAtIndex: [edit numberOfItems] - 1];
if ([[edit itemAtIndex: [edit numberOfItems] - 1] isSeparatorItem])
[edit removeItemAtIndex: [edit numberOfItems] - 1];
}
ПРИМЕЧАНИЕ: этот код должен войти в applicationDidFinishLaunching:
или позже, если вы поместите его в applicationWillFinishLaunching:
записи еще не будут добавлены в Edit
меню.
Также обратите внимание, я использую NSSelectorFromString
как используя @selector
вызывает предупреждения "неизвестный селектор". (Даже с предупреждением код работает, но я предпочитаю, чтобы в моем коде не было предупреждений, поэтому я решил использовать NSSelectorFromString
чтобы избежать их.)
Самый быстрый способ исправить это - установить заголовок "Редактировать" (с дополнительным пробелом в конце).
В конструкторе интерфейса выберите меню "Правка":
Затем из инспектора свойств добавьте дополнительный пробел к заголовку.
Решение для Swift 4 с использованием раскадровок
Добавьте следующий код к вашему AppDelegate
:
func applicationWillFinishLaunching(_ notification: Notification) {
UserDefaults.standard.set(true, forKey: "NSDisabledDictationMenuItem")
UserDefaults.standard.set(true, forKey: "NSDisabledCharacterPaletteMenuItem")
}
applicationWillFinishLaunching
Функция вызывается в начале жизненного цикла вашего приложения, до того, как меню будет инициализировано. Не нужно вручную взламывать пункты меню.
Из трех уже упомянутых подходов настройка, вероятно, является наиболее чистой, хотя, как уже отмечалось, она полагается на недокументированные
UserDefaults
записи.
Подход удаления элементов по позиции индекса (т. Е. Последней позиции) в меню основан на предположении, что они всегда будут в конце меню. Это, вероятно, безопасное предположение, но в будущем Apple может сделать что-то еще.
Использование названий пунктов меню также проблематично по причинам локализации.
Лучше использовать селектор, как показано для Objective-C, но он не будет работать так просто в Swift, особенно для пункта меню «Начать диктовку ...». Метод, для которого вам нужен селектор, -
startDictation(_:)
, но, в отличие от Objective-C, вы не можете просто так напечатать его. Вам необходимо указать
@objc
тип, к которому он принадлежит. Так что просто ищите документацию Apple, не так ли? Удачи. Это недокументировано, и метод не отображается в Swift.
Мой подход к решению этой проблемы - добавить заглушку для этого метода в my, а затем использовать ее для получения селектора. Вам действительно просто нужен какой-то тип, который Swift может использовать, чтобы сформировать селектор. Но когда селектор перебрасывается через стену в сторону Obj-C какао, этот тип просто исчезает. Все, что имеет значение, - это имя метода, количество параметров, их порядок и имена. В этом случае, как и в большинстве методов действий, он принимает один параметр (для отправителя):
@objc public func startDictation(_: Any) { }
Это удобно для быстрой и грязной реализации, но для реального использования я предпочитаю создать класс, наследующий от
NSObject
с
private
инициализаторы специально для таких методов-заглушек. Таким образом вы гарантируете, что они никогда не загрязнят цепочку респондентов. По сути, создайте пакет бездействующих методов, которые не могут быть инстанциированы, которые вы можете использовать для создания селекторов.
Теперь нам нужен способ найти соответствующий пункт меню, поэтому я делаю расширение на
public extension NSMenu
{
func lastMenuItem(where condition: (NSMenuItem) -> Bool) -> NSMenuItem?
{
for item in items.reversed()
{
if let submenu = item.submenu
{
if let foundItem = submenu.lastMenuItem(where: condition) {
return foundItem
}
}
else if condition(item) { return item }
}
return nil
}
}
Я выбираю поиск в меню в обратном порядке, исходя из предположения, что если я решу добавить свои собственные элементы в какой-то момент в будущем, они, скорее всего, будут раньше элементов, которые я удаляю. Затем в
AppDelegate
:
func removeUnwantedAutomaticMenus()
{
let unwantedActions: [Selector] =
[
#selector(AppDelegate.startDictation(_:)),
#selector(NSApplication.orderFrontCharacterPalette(_:)),
]
for action in unwantedActions {
NSApp.mainMenu?.lastMenuItem { $0.action == action }?.isHidden = true
}
}
Как видите, я предпочитаю скрывать элементы, а не удалять их, но вместо этого вы, безусловно, можете удалить их. Все, что осталось, это просто позвонить
removeUnwantedAutomaticMenus()
в
AppDelegate.applicationDidFinishLaunching
.
Если вы создаете свои меню программно, вы можете сделать его более надежным, используя
tag
, чтобы отметить добавленные вами элементы, затем проверьте наличие этого тега, чтобы убедиться, что вы не удаляете / не скрываете их вместо автоматически добавленных.
Другой подход - создать подкласс
NSMenu
, отвергая его
addItem
и
insertItem
методы для проверки тега перед их добавлением. Просто не добавляйте / не вставляйте
NSMenuItem
s с неправильным тегом, которого не будет у автоматически вставленных элементов Apple. Если вы используете раскадровки для своих меню, это своего рода боль, потому что вы должны убедиться, что класс для каждого меню установлен на ваш собственный класс, а тег для каждого элемента меню установлен правильно. Если вы создаете свои меню программно, гораздо проще убедиться, что все настроено правильно.
Для Swift 4 и Xcode 9.2 это будет:
static let EDIT_MENU_TITLE = "Edit"
static let SPECIAL_CHARACTERS_TITLE = "Emoji & Symbols"
static let DICTATION_MENU_TITLE = "Start Dictation…"
А затем я использую следующую функцию, чтобы включить / отключить меню "Правка":
func enableEditingMenu( enabled: Bool ) {
let m = NSApplication.shared().mainMenu
let mi = m?.item(withTitle: MenuController.EDIT_MENU_TITLE )
mi?.isEnabled = enabled
if (mi != nil) { // Edit-Menu exists, otherwise you would run into an exception when proceeding
let Count: Int = mi!.submenu!.numberOfItems
if (mi!.submenu!.item(at: Count - 1)!.title == MenuController.SPECIAL_CHARACTERS_TITLE) {
mi!.submenu!.removeItem(at: Count - 1)
}
if (mi!.submenu!.item(at: Count - 2)!.title == MenuController.DICTATION_MENU_TITLE) {
mi!.submenu!.removeItem(at: Count - 2)
}
if (mi!.submenu!.item(at: Count - 3)!.title == "") {
mi!.submenu!.removeItem(at: Count - 3)
}
}
}
Который называется так в контроллере представления, где мне нужно включить / отключить:
override func viewWillAppear() {
let mc = MenuController()
mc.enableEditingMenu( enabled: true )
}
override func viewWillDisappear() {
let mc = MenuController()
mc.enableEditingMenu( enabled: false )
}
Я также гарантирую, что он отключен из AppDelegate.swift
В Apple Swift вы можете сделать это так:
var EditMenu = NSApplication.sharedApplication().mainMenu!.itemWithTitle("Edit")
if (EditMenu != nil) // Edit-Menu exists, otherwise you would run into an exception when proceeding
{
var Count: Int = EditMenu!.submenu!.numberOfItems
if (EditMenu!.submenu!.itemAtIndex(Count - 1)!.title == "Special Characters…")
{
EditMenu!.submenu!.removeItemAtIndex(Count - 1)
}
if (EditMenu!.submenu!.itemAtIndex(Count - 2)!.title == "Start Dictation…")
{
EditMenu!.submenu!.removeItemAtIndex(Count - 2)
}
println("Titel = '\(EditMenu!.submenu!.itemAtIndex(Count - 3)!.title)'")
if (EditMenu!.submenu!.itemAtIndex(Count - 3)!.title == "")
{
EditMenu!.submenu!.removeItemAtIndex(Count - 3)
}
}
Просто замените "Edit" на "Bearbeiten" для немецкой версии. Пункт меню Разделитель возвращает пустую строку в качестве заголовка.