Reputation: 11
In the Edit
section of the top menu bar in MacOS, it keeps on adding Start Dictation...
and Emoji & Symbols
, even though it's not there in Xcode. Here's a screenshot
with the last two, unwanted items circled in red. I can't delete them, because they don't exist in the Xcode UI builder, but they get added automatically somehow. How can I get rid of them?
I am not allowed to programatically delete them at application launch. I need to prevent them in the first place.
I tried to replace the current edit menu with a freshly created one, deleting the old one, and transferring the items, but with no prevail. I also tried to rename it to another name, also with no prevail.
My code is in Swift, so the ObjC answers can't help me, and on top of that I'm not allowed to manually delete them after the program starts programmatically.
Thanks ahead of time!
Upvotes: 1
Views: 890
Reputation: 2785
I posted an answer that uses Swift with selectors to the question that Willeke linked to. I won't repeat that solution here, but at the end I mentioned an alternative approach that I will say more about here.
First I'll say this solution is a bit heavy-weight, but it's also robust against any future automatic menus Apple might choose to add. It's best suited for programmatically creating your menus. You can use it with Storyboards, but it's more of a pain.
The idea is to prevent the unwanted menus from being added in the first place without relying on undocumented UserDefaults
settings.
To accomplish this, you need to take control of the process of adding menu items. That happens in NSMenu
, so the plan would be to subclass it, overriding the various addItem
/insertItem
methods to check the tag
property of any NSMenuItem
being added. If the tag doesn't match some value you define for your app, simply refuse to add the item.
Unfortunately NSMenu
doesn't doesn't call addItem(_:)
when you call addItem(withTitle:action:keyEquivalent)
, nor for the insertItem
methods, so you have to override all of them not just two.
It's also helpful to do some debug printing, especially if you are using Storyboards, because it's easy to miss tagging menu items.
class TaggedItemMenu: NSMenu
{
static let wantedTag = 42 // or whatever value
// Helper for creating properly tagged menu items
private func makeTaggedItem(
withTitle string: String,
action selector: Selector?,
keyEquivalent charCode: String) -> NSMenuItem
{
let newItem = NSMenuItem(
title: string,
action: selector,
keyEquivalent: charCode
)
newItem.tag = Self.wantedTag
return newItem
}
// If you use Storyboards, you have to individually set all the tags, so
// its helpful to log untagged add/inserts so you can check they're not one
// of your menu items you missed setting the tag for.
private func logUntaggedAddInsert(
_ item: @autoclosure () -> NSMenuItem,
function: StaticString = #function)
{
#if DEBUG
print("Call to \(function) for untagged NSMenuItem named \"\(item().title)\"")
#endif
}
// MARK: Methods for your app to use
// -------------------------------------
public override func addItem(_ newItem: NSMenuItem)
{
guard newItem.tag == Self.wantedTag else
{
logUntaggedAddInsert(newItem)
return
}
super.addItem(newItem)
}
// Replacement for addItem(withTitle:action:keyEquivalent)
public func addTaggedItem(
withTitle string: String,
action selector: Selector?,
keyEquivalent charCode: String) -> NSMenuItem
{
let newItem = makeTaggedItem(
withTitle: string,
action: selector,
keyEquivalent: charCode
)
super.addItem(newItem)
return newItem
}
public override func insertItem(_ newItem: NSMenuItem, at index: Int)
{
guard newItem.tag == Self.wantedTag else
{
logUntaggedAddInsert(newItem)
return
}
super.insertItem(newItem, at: index)
}
// Replacement for insertItem(withTitle:action:keyEquivalent:at)
public func insertTaggedItem(
withTitle string: String,
action selector: Selector?,
keyEquivalent charCode: String,
at index: Int) -> NSMenuItem
{
let newItem = makeTaggedItem(
withTitle: string,
action: selector,
keyEquivalent: charCode
)
super.insertItem(newItem, at: index)
return newItem
}
// MARK: Do NOT use these methods in your app
// These will be used when macOS automatically inserts menus items.
// -------------------------------------
public override func addItem(
withTitle string: String,
action selector: Selector?,
keyEquivalent charCode: String) -> NSMenuItem
{
let newItem = NSMenuItem(
title: string,
action: selector,
keyEquivalent: charCode
)
logUntaggedAddInsert(newItem)
return newItem
}
public override func insertItem(
withTitle string: String,
action selector: Selector?,
keyEquivalent charCode: String,
at index: Int) -> NSMenuItem
{
let newItem = NSMenuItem(
title: string,
action: selector,
keyEquivalent: charCode
)
logUntaggedAddInsert(newItem)
return newItem
}
}
If programmatically creating menus, you only create TaggedItemMenu
s instead of NSMenu
, and ensure you only ever create menus items with the addTaggedItem
and insertTaggedItem
. This way the automatically added menus never get into your menus in the first place, and then you don't need to worry about removing them later.
Also keep in mind that when you add submenus, those are basically a menu wrapped in a menu item. The menu part of the submenu needs to be a TaggedItemMenu
too, and the NSMenuItem
it's wrapped inside of needs to be tagged or it won't be added. Whether or not you need to tag your menu items, it's useful to add a addSubmenu
and insertSubmenu
to NSMenu
via extension
(or in this case to TaggedItemMenu
), to do that wrapping for you.
If you're using Storyboards, you have to make sure to change each menu's and submenu's class to TaggedItemMenu
in its "Identity Inspector",
and individually set the tag for each menu item in its "Attribute Inspector"
Setting Menu Item tag
property
That doesn't seem too bad until you starting counting up all the items in menus and submenus, all just to get rid of a couple of items Apple decided to inject into your program. Of course, if you add new menu items later, you'll need to make sure you set their tag
s. That's why the logging comes in handy.
If you can't live with the automatically added menu items, I'd recommend either moving away from Storyboards, or take one of the approaches to remove the items after the fact, and just accept that it could break in some future macOS version.
Upvotes: 2