要在我的Cocoa应用程序中启用复制和粘贴,我在菜单中添加了两个新菜单项(复制和粘贴),并将选择器从每个项目拖动到第一个响应者(复制和粘贴)。但是,复制和粘贴菜单项下方会显示两个额外的项目:“开始听写”和“特殊字符”。
我无法弄清楚它们出现的原因或我如何删除它们。
最理想的是,我甚至不希望复制和粘贴菜单项可见。我只是希望我的应用程序的用户能够将内容(即从电子邮件,文本文档等)粘贴到我的应用程序中的一个表单上的文本字段中。
答案 0 :(得分:11)
以下是我在我的应用程序中使用的代码,用于将这些自动添加的条目删除到“编辑”菜单中:
- (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
来避免它们。)
答案 1 :(得分:8)
如Mac OS X Internals: A Systems Approach和Qt Mac (Re)move "Special Characters..." action in Edit menu中所述,您可以在加载nib之前在main()中执行类似的操作(但不支持API):
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"NSDisabledDictationMenuItem"];
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"NSDisabledCharacterPaletteMenuItem"];
答案 2 :(得分:4)
解决此问题的最快方法是将标题设置为"编辑" (最后有一个额外的空间)。
在界面构建器中,选择“编辑”菜单:
然后从属性检查器中,为标题添加一个额外的空格。
答案 3 :(得分:1)
在已经提到的三种方法中,设置 UserDefaults
可能是最干净的,但如前所述,它依赖于未记录的 UserDefaults
条目。
通过菜单中的索引位置(即最后一个位置)删除项目的方法依赖于这样的假设,即它们将始终位于菜单的末尾。这可能是一个安全的假设,但苹果未来可能会做其他事情。
由于本地化原因,依赖菜单项名称也存在问题。
最好使用选择器,如 Objective-C 所示,但它在 Swift 中不会那么简单,特别是对于“开始听写...”菜单项。您需要选择器的方法是 startDictation(_:)
,但与 Objective-C 不同的是,您不能像那样输入它。您需要指定它所属的 @objc
类型。所以只需搜索Apple的文档,对吗?祝你好运。它没有记录,并且该方法未在 Swift 中公开。
我解决该问题的方法是在我的 AppDelegate
中为该方法添加一个存根,然后使用它来获取选择器。你真的只需要一些 Swift 可以使用的类型来形成选择器。 But when the selector is tossed over the wall to the Obj-C side of Cocoa, that type just disappears.重要的是方法的名称、参数的数量、它们的顺序和名称。在这种情况下,像大多数动作方法一样,它需要一个参数(对于发送者):
@objc public func startDictation(_: Any) { }
AppDelegate
对于快速而肮脏的实现很方便,但在实际使用中,我更喜欢创建一个继承 NSObject
的类,并带有 private
初始化程序,专门用于此类存根方法。这样您就可以确保它们永远不会污染响应者链。基本上是制作一个不可实例化的包,你可以用它来制作选择器。
现在我们需要一种方法来找到对应的菜单项,所以我在NSMenu
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
中的 NSMenuItem
属性来标记您添加的项目,然后检查该标签以确保您不会删除/隐藏那些而不是自动添加的。
另一种方法是对 NSMenu
进行子类化,覆盖其 addItem
和 insertItem
方法以在添加之前检查 NSMenuItem
的标签。只需不要添加/插入任何带有错误标签的 NSMenuItem
,Apple 自动插入的项目不会有这些标签。如果您在菜单中使用 Storyboard,那会有点麻烦,因为您必须确保每个菜单的类都设置为您的自定义类,并且每个菜单项的标签设置正确。如果您以编程方式创建菜单,则可以更轻松地确保一切设置正确。
答案 4 :(得分:0)
使用情节提要的Swift 4解决方案
将以下代码添加到您的AppDelegate
:
func applicationWillFinishLaunching(_ notification: Notification) {
UserDefaults.standard.set(true, forKey: "NSDisabledDictationMenuItem")
UserDefaults.standard.set(true, forKey: "NSDisabledCharacterPaletteMenuItem")
}
在初始化菜单之前,将在应用程序生命周期的早期调用applicationWillFinishLaunching
函数。无需手动修改菜单项。
答案 5 :(得分:-1)
在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)
}
}
只需将“编辑”替换为德语版的“Bearbeiten”即可。分隔符菜单项返回一个空字符串作为标题。
答案 6 :(得分:-1)
对于Swift 4和Xcode 9.2,这将是:
switch
然后我使用以下功能启用/禁用编辑菜单:
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)
}
}
}
我还确保在AppDelegate.swift
中禁用它