NSMenu and NSStatusItem action wont work together
"If the status item has a menu set, the action is not sent to the target when the status item is clicked; instead, the click causes the menu to appear."- apple dev NSStatusItem.action
refresh NSMenuItem on click/open of NSStatusItem
Keep a reference to the created NSMenuItem
in your app delegate and update its state (assuming you use the item only in a single menu).
class AppDelegate: NSApplicationDelegate {
var fooMenuItem: NSMenuItem?
}
func createStatusBarItem() {
...
let enableDisableMenuItem = NSMenuItem(title: "Enabled", action: #selector(toggleAdvancedMouseHandlingObjc), keyEquivalent: "e")
self.fooMenuItem = enableDisableMenuItem
...
}
@objc func toggleAdvancedMouseHandlingObjc() {
if sHandler.isAdvancedMouseHandlingEnabled() {
sHandler.disableAdvancedMouseHandling()
} else {
sHandler.enableAdvancedMouseHandling()
}
self.fooMenuItem.state = sHandler.isAdvancedMouseHandlingEnabled() ? NSControl.StateValue.on : NSControl.StateValue.off
}
How to bind selector to method inside NSObject
Setting the button's action
is only one half of what you need to do. You also need to specify a target
. Add
self.item.button?.target = self
and I believe you will get the result you are looking for.
What's happening is action
specifies the selector to invoke and target
specifies the object on which to invoke it.
Action of StatusItem not working in Swift
If the button is showing up and nothing is happening when you click it, it looks to me like you need to make sure you're setting your button's target
to your App
instance. E.g.:
button.target = self
Otherwise the action is only followed up the responder chain.
NSMenuItem with action added to NSStatusBar is grayed out when the selected function is moved to other class
The solution was to manually set the target property to self
as @vadian and @red_menace helped me discover in the comments of the question.
The working code looks like this:
import Cocoa
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
var statusBarItemController: StatusBarItemController?
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
statusBarItemController = StatusBarItemController()
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
}
import Cocoa
class StatusBarItemController: NSObject {
let statusBarItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength)
override init() {
super.init()
if let button = statusBarItem.button {
button.image = NSImage(named: NSImage.Name("trayIcon"))
}
constructMenu()
}
func constructMenu() {
if let button = statusBarItem.button {
button.image = NSImage(named: NSImage.Name("trayIcon"))
}
let menu = NSMenu()
let fromTheWindowsItem = NSMenuItem(
title: "From the windows...",
action: #selector(StatusBarItemController.printToTheWalls(_:)),
keyEquivalent: ""
)
fromTheWindowsItem.target = self
menu.addItem(fromTheWindowsItem)
menu.addItem(NSMenuItem.separator())
menu.addItem(NSMenuItem(
title: "Quit",
action: #selector(NSApplication.terminate(_:)),
keyEquivalent: "q"
))
statusBarItem.menu = menu
}
@objc func printToTheWalls(_ sender: NSMenuItem) {
print("To the Walls!")
}
}
Call Action when NSStatusBarButton is right-clicked
You can subclass and override the mouseDown
method, but since Mac OS X 10.10 (Yosemite), there has been an easier way: NSGestureRecognizer
and its subclasses:
func applicationDidFinishLaunching(aNotification: NSNotification) {
// Insert code here to initialize your application
if let button = statusItem.button {
button.image = NSImage(named: "myImage")
button.alternateImage = NSImage(named: "myImage")
button.action = Selector("myAction")
// Add right click functionality
let gesture = NSClickGestureRecognizer()
gesture.buttonMask = 0x2 // right mouse
gesture.target = self
gesture.action = "myRightClickAction:"
button.addGestureRecognizer(gesture)
}
}
func myRightClickAction(sender: NSGestureRecognizer) {
if let button = sender.view as? NSButton {
// Handle your right click event here
}
}
How can I call a function/action when a statusItem is clicked?
I was able to reach an implementation shown below, using the new NSStatusBarButton visual representation of an NSStatusBarItem. In this example, my .xib file has the NSPopover
element already connected to a view, which isn't shown here.
@IBOutlet weak var myPopover: NSPopover!
var statusBar: NSStatusItem!
var popoverIsOpen = false
@IBAction func togglePopover(sender: AnyObject) {
if !(popoverIsOpen) {
myPopover.showRelativeToRect(sender.bounds, ofView: statusBar.button, preferredEdge: NSRectEdge(3))
popoverIsOpen = true
}
else {
myPopover.close()
popoverIsOpen = false
}
}
func applicationDidFinishLaunching(aNotification: NSNotification?) {
//initialize menu bar icon
statusBar = NSStatusBar.systemStatusBar().statusItemWithLength(CGFloat(48))
statusBar.button.title = "Your App Title"
statusBar.button.appearsDisabled = false
statusBar.button.action = Selector("togglePopover:")
statusBar.button.target = self
}
Related Topics
Why Can't I Divide Integers in Swift
How to Hide the Navigationbar When Embedding Swiftui in Uikit
What Is the "@Exported" Attribute in Swift
Po Swift String "Unresolved Identifier"
In Swift, Does Resetting the Property Inside Didset Trigger Another Didset
Unsafemutablepointer in Swift as Replacement for Properly Sized C Array in Obj-C
Fbsdkapplicationdelegate Application Openurl:Sourceapplication:Annotation Deprecated
Change Tabview Indicator Swiftui
How to Make List with Single Selection with Swiftui
Blue Highlighting/Focus Ring on Catalyst App
How to Open File Dialog with Swiftui on Platform "Uikit for MAC"
Remove Multiple Indices from Array
Firebase Says That My Rules Are Insecure, Why
How to Encode Realm's List<> Type
Getting Path for Resource in Command Line Tool