How do I add a label to NSMenuItem that is disabled but not greyed out.
let addToComputerItem : NSMenuItem = NSMenuItem(title: "\(title)", action: nil, keyEquivalent: "")
computerInfoMenu.addItem(addToComputerItem)
addToComputerItem.enabled = false
Try this:
let menu = NSMenuItem(title: "Test", action: #selector(AppDelegate.test), keyEquivalent: "K")
menu.attributedTitle = NSAttributedString(string: "Testinggg", attributes: [NSFontAttributeName: NSFont.systemFontOfSize(20), NSForegroundColorAttributeName: NSColor.redColor()])
mainMenu?.itemAtIndex(0)?.submenu?.addItem(menu)//You can add whichever submenu
Related
I have a menu with menu items. The problem is that my menu items are all greyed out or not enabled
public override init() {
super.init()
let menu = NSMenuItem(title: "Debug", action: nil, keyEquivalent: "")
menu.submenu = NSMenu(title: "Debug")
menu.submenu?.addItem(withTitle: "Load saved data", action: #selector(loadDataFromFile(_:)), keyEquivalent: "");
menu.submenu?.addItem(withTitle: "another item", action: #selector(loadDataFromFile(_:)), keyEquivalent: "")
menu.isEnabled = true
NSApplication.shared.mainMenu?.addItem(menu)
}
#objc func loadDataFromFile(_ sender: Any) {
print("load it")
}
To be able to call a custom selector in the current class you have to set the target of the menu item to self
let menu = NSMenuItem(title: "Debug", action: nil, keyEquivalent: "")
menu.submenu = NSMenu(title: "Debug")
let loadItem = NSMenuItem(title: "Load saved data", action: #selector(loadDataFromFile), keyEquivalent: "")
loadItem.target = self
let anotherItem = NSMenuItem(title: "another item", action: #selector(loadDataFromFile), keyEquivalent: "")
anotherItem.target = self
menu.submenu?.addItem(loadItem)
menu.submenu?.addItem(anotherItem)
menu.isEnabled = true
I am creating a custom UIToolBar to add as inputAccessoryView to a UITextField, I would like to add a UIBarButtonItem on the right side of this toolbar to serve as the return key of this textfield, and the text of this barButtonItem should be the same as the keyboard of that textfield.
My approach was:
let buttonDone = UIBarButtonItem(title: myTextField.returnKeyType, style: .done, target: self, action: #selector(pickerDone)
let space = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
// toolbar
let toolBar = UIToolbar()
toolBar.barStyle = .default
toolBar.items = [space, buttonDone]
toolBar.sizeToFit()
// setup input
myTextField.inputAccessoryView = toolBar
But with this i get this error:
Cannot convert value of type 'UIReturnKeyType?' to expected argument type 'String?'
So, I try something like:
title: myTextField.returnKeyType.text
But returnKeyType doesn't has a .text variable or similar...
Is there any way of doing this?
Should I go another way?
There is no built-in way to convert the UIReturnKeyType enum to a string. You will need to write your own code using a switch on the all the possible values.
Here's one solution using an extension. Add support for the other values as needed.
extension UIReturnKeyType {
var label: String {
switch self {
case .default:
return "Return"
case .go:
return "Go"
case .done:
return "Done"
default:
return "Enter"
}
}
}
Then you can use this as:
let buttonDone = UIBarButtonItem(title: myTextField.returnKeyType.label, style: .done, target: self, action: #selector(pickerDone)
I'm wanting to call a function and give it parameters using the #selector. However, I get the error:
"Argument of '#selector' does not refer to an '#objc' method, property, or initializer"
#objc func changeCrypto(crypto: String) {
//stuff
}
func constructMenu() {
let menu = NSMenu()
menu.addItem(NSMenuItem(title: "Bitcoin", action: #selector(changeCrypto(crypto: "bitcoin")), keyEquivalent: "B"))
menu.addItem(NSMenuItem(title: "Ethereum", action: #selector(changeCrypto(crypto: "ethereum")), keyEquivalent: "E"))
menu.addItem(NSMenuItem(title: "Litecoin", action: #selector(changeCrypto(crypto: "litecoin")), keyEquivalent: "L"))
menu.addItem(NSMenuItem.separator())
menu.addItem(NSMenuItem(title: "Quit It", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q"))
statusItem.menu = menu
}
I fixed it! I changed it to the following..
#objc func changeCrypto(_ sender: NSMenuItem) {
//Here I call the title of the Menu Item pressed
print(sender.title)
}
func constructMenu() {
let menu = NSMenu()
menu.addItem(NSMenuItem(title: "Bitcoin", action: #selector(changeCrypto(_:)), keyEquivalent: "B"))
menu.addItem(NSMenuItem(title: "Ethereum", action: #selector(changeCrypto(_:)), keyEquivalent: "E"))
menu.addItem(NSMenuItem(title: "Litecoin", action: #selector(changeCrypto(_:)), keyEquivalent: "L"))
menu.addItem(NSMenuItem.separator())
menu.addItem(NSMenuItem(title: "Quit It", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q"))
statusItem.menu = menu
}
As the title says, when my status bar menu is open and a NSAlert is triggered from another thread, the UI freezes.
Presumably this is because both things are running on the main thread. But since I'm dealing with an NSAlert and an NSMenu, don't I have to run these on the main thread?
NSAlert Code
func showWallpaperUpdateErrorAlert(messageText: String, informativeText: String) {
DispatchQueue.main.async {
NSApp.activate(ignoringOtherApps: true)
let updateErrorAlert = NSAlert()
updateErrorAlert.messageText = messageText
updateErrorAlert.informativeText = informativeText
updateErrorAlert.addButton(withTitle: "OK")
updateErrorAlert.runModal()
}
}
NSMenu Code
func createStatusBarMenu() {
// Status bar icon
guard let icon = NSImage(named: "iconFrame44")
else { NSLog("Error setting status bar icon image."); return }
icon.isTemplate = true
statusBarItem.image = icon
// Create Submenu items
let viewOnRedditMenuItem = NSMenuItem(title: "View on Reddit...", action: #selector(viewOnRedditAction), keyEquivalent: "")
viewOnRedditMenuItem.target = self
let saveThisImageMenuItem = NSMenuItem(title: "Save This Image...", action: #selector(saveThisImageAction), keyEquivalent: "")
saveThisImageMenuItem.target = self
// Add to title submenu
let titleSubmenu = NSMenu(title: "")
titleSubmenu.addItem(descriptionMenuItem)
titleSubmenu.addItem(NSMenuItem.separator())
titleSubmenu.addItem(viewOnRedditMenuItem)
titleSubmenu.addItem(saveThisImageMenuItem)
// Create main menu items
titleMenuItem = NSMenuItem(title: "No Wallpaperer Image", action: nil, keyEquivalent: "")
titleMenuItem.submenu = titleSubmenu
titleMenuItem.isEnabled = false
getNewWallpaperMenuItem = NSMenuItem(title: "Update Now", action: #selector(getNewWallpaperAction), keyEquivalent: "")
getNewWallpaperMenuItem.target = self
let preferencesMenuItem = NSMenuItem(title: "Preferences...", action: #selector(preferencesAction), keyEquivalent: "")
preferencesMenuItem.target = self
let quitMenuItem = NSMenuItem(title: "Quit Wallpaperer", action: #selector(quitAction), keyEquivalent: "")
quitMenuItem.target = self
// Add to main menu
let statusBarMenu = NSMenu(title: "")
statusBarMenu.addItem(titleMenuItem)
statusBarMenu.addItem(NSMenuItem.separator())
statusBarMenu.addItem(getNewWallpaperMenuItem)
statusBarMenu.addItem(NSMenuItem.separator())
statusBarMenu.addItem(preferencesMenuItem)
statusBarMenu.addItem(quitMenuItem)
statusBarItem.menu = statusBarMenu
statusBarMenu.delegate = self
}
In my case the solution was to dismiss the menu before showing the alert.
I had to access the menu from the NSStatusItem's menu property and call cancelTrackingWithoutAnimation() (regular cancelTracking() wasn't as smooth). I also had to do this outside the main thread, for whatever reason.
func showWallpaperUpdateErrorAlert(messageText: String, informativeText: String) {
statusBarItem.menu?.cancelTrackingWithoutAnimation() // This is new
DispatchQueue.main.async {
NSApp.activate(ignoringOtherApps: true)
let updateErrorAlert = NSAlert()
updateErrorAlert.messageText = messageText
updateErrorAlert.informativeText = informativeText
updateErrorAlert.addButton(withTitle: "OK")
updateErrorAlert.runModal()
}
}
I'm currently working on a "menu bar only" project and I need to list all mounted Volumes in a submenu in the menubar app. I figured out how to print() all mounted volumes but I need help with the submenu (without .xib or .storyboard work)
This is my "listVolumes func"
func listVolumes(sender: NSMenuItem) {
let keys = [NSURLVolumeNameKey, NSURLVolumeIsRemovableKey, NSURLVolumeIsEjectableKey]
let paths = NSFileManager().mountedVolumeURLsIncludingResourceValuesForKeys(keys, options: [])
if let urls = paths {
for url in urls {
if let components = url.pathComponents
where components.count > 1
&& components[1] == "Volumes" {
print(url)
}
}
}
}
an below is my code for the "menu bar app"
let statusItem = NSStatusBar.systemStatusBar().statusItemWithLength(-2)
func applicationDidFinishLaunching(aNotification: NSNotification) {
if let button = statusItem.button {
button.image = NSImage(named: "StatusBarButtonImage")
}
let menu = NSMenu()
menu.addItem(NSMenuItem(title: "Volumes", action: Selector("volumes:"), keyEquivalent: ""))
menu.addItem(NSMenuItem.separatorItem())
menu.addItem(NSMenuItem(title: "Help", action: Selector("help:"), keyEquivalent: ""))
menu.addItem(NSMenuItem.separatorItem())
menu.addItem(NSMenuItem(title: "Quit", action: Selector("terminate:"), keyEquivalent: "q"))
statusItem.menu = menu
}
so my question is, how can I create a submenu that contains all the mounted drives (programmatically only)
You have to set a submenu to the volumes menu item for example
let volumesMenuItem = NSMenuItem(title: "Volumes", action: Selector("volumes:"), keyEquivalent: "")
menu.addItem(volumesMenuItem)
let volumesMenu = NSMenu(title: "Volumes")
volumesMenuItem.submenu = volumesMenu
I recommend to return an array of the names from your listVolumes() function, then you can add menu items to volumesMenu assuming volumes contains the names
for volumeName in volumes {
volumesMenu.addItem(NSMenuItem(title: volumeName, action: Selector("selectVolume"), keyEquivalent: ""))
}