I've tried setting "Application is Agent" to 1 but the status bar item disappears. When set to 0 both the status bar item and dock icon are showing. How do I show the status bar item but hide the dock icon?
I've tried the following in both awakeFromNib() and applicationDidFinishLaunching() in AppDelegate.swift:
//class scope
let statusItem = NSStatusBar.system().statusItem(withLength: NSVariableStatusItemLength)
//function scope
self.statusItem.image = NSImage(named: "myImage")
let menu = NSMenu(title: "MyApp-Menu")
let menuItem = NSMenuItem(title: "title", action: nil, keyEquivalent: "")
menu.addItem(menuItem)
self.statusItem.menu = menu
Updated for Mac OS 10.14
in AppDelegate:
let statusItem = NSStatusBar.system.statusItem(withLength:NSStatusItem.squareLength)
func applicationDidFinishLaunching(_ aNotification: Notification) {
if let button = statusItem.button {
button.image = NSImage(named:NSImage.Name("StatusBarButtonImage"))
button.action = #selector(launchFromTray)
}
constructMenu()
}
func constructMenu() {
let menu = NSMenu()
menu.addItem(NSMenuItem.separator())
menu.addItem(NSMenuItem(title: "Show", action: #selector(launchFromTray), keyEquivalent: "w"))
menu.addItem(NSMenuItem(title: "Quit", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q"))
statusItem.menu = menu
}
And you should set the Application is agent (UIElement) to YES now. Should work fine, hope this will help someone
You want hide the dock icon, you must set "Application is Agent" to "YES" or you can set app to UIElement Application:
ProcessSerialNumber psn = { 0, kCurrentProcess };
OSStatus rt = TransformProcessType(&psn, kProcessTransformToUIElementApplication);
According to the parts of codes you're offered I think that you say "the status bar item disappears" is not really disappear, it is just the menu disappears. The status bar can't disappear unless you write the wrong code, but your codes look fine.
If what I think is right, the menu disappears because the app is an agent app. What you need to do is just make it be a UI Element app again:
ProcessSerialNumber psn = { 0, kCurrentProcess };
OSStatus returnCode = TransformProcessType(&psn, kProcessTransformToForegroundApplication);
You can't show a menu bar when you run an agent app.Only one can be selected between the agent app and the menu bar.
Agents include background-only applications, faceless background-only applications, and UI elements, but is not a full blown application with a menu bar
Related
I have a swift app for MacOS, and have a menu with sub menuitems. I add the menu from the appdelegate and assign an action via the interface builder, but the target action is never called:
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
if let menu = menu
{
statusItem?.menu = menu
menu.delegate = self
}
pauseMenuItem.target = self
pauseMenuItem.action = #selector(pausePressed(_:))
We can see from the bullet on the left of IBAction and from the InterfaceBuilder that the link is well done, but whenever I press on the corresponding menuitem, the action is not executed:
What am I missing ?
I ended up creating the menu programmatically and it worked:
let menu = NSMenu()
let pauseButton = NSMenuItem(title: "Pause", action: #selector(pausePressed), keyEquivalent: "")
menu.addItem(pauseButton)
I am making a status bar macOS app. On clicking on the status bar icon, I am showing an NSPopover (not an NSMenu). However, when my NSPopover is shown, my status menu icon is not highlighted. It is only highlighted for a moment when I click it. I want it to stay highlighted, much like how it behaves with the wifi status bar icon.
I know that if I use a NSMenu instead of a NSPopover, it can probably be fixed. But the requirement is such that I need to use a NSPopover.
I have tried the following approaches, but to no avail:
1.
let statusItem = NSStatusBar.system.statusItem(withLength:NSStatusItem.squareLength)
if let button = statusItem.button {
button.setButtonType(.pushOnPushOff)
}
statusItem.highlightMode = true
statusItem.button?.highlight(true)
statusItem.button?.isHighlighted = true
I am not very experienced with status bar apps. So I am not really sure about which approach to take here.
The left most icon is my status bar app icon. The popover is currently active but the icon is not highlighted. I had to crop out the popover.
This can be done, but to do it reliably requires some tight coupling. In this example, I assume you have a NSViewController named PopoverController that has a togglePopover() method and that you've set this controller as the target of the status bar button.
Step 0: Context
Here's the basic setup of the class that controls the popover:
final class PopoverController: NSViewController
{
private(set) var statusItem: NSStatusItem!
private(set) var popover: NSPopover
// You can set up your StatusItem and Popover anywhere; but here's an example with -awakeFromNib()
override func awakeFromNib()
{
super.awakeFromNib()
statusItem = NSStatusBar.system.statusItem(withLength: 20.0)
statusItem.button?.sendAction(on: [.leftMouseDown, .rightMouseDown])
statusItem.button?.image = NSImage(named: "statusBar-icon")
statusItem.button?.target = self
statusItem.button?.action = #selector(togglePopover)
popover = NSPopover()
popover.behavior = .transient
popover.delegate = self
popover.contentViewController = self
popover.setValue(true, forKeyPath: "shouldHideAnchor") // Private API
}
#objc func togglePopover()
{
if popover.isShown
{
popover.performClose(nil)
}
else if !popover.isShown,
let button: NSButton = statusItem.button,
button.window?.contentView != nil, // Exception thrown if view is nil or not part of a window.
button.superview != nil
{
popover.show(relativeTo: .zero, of: button, preferredEdge: .minY)
}
}
}
Step 1: Override the Status Button
extension NSStatusBarButton
{
public override func mouseDown(with event: NSEvent)
{
if event.modifierFlags.contains(.control)
{
self.rightMouseDown(with: event)
return
}
if let controller: PopoverController = self.target as? PopoverController
{
controller.togglePopover()
self.highlight(controller.popover.isShown)
}
}
}
Step 2: Handle Popover Closing
Make sure PopoverController conforms to NSPopoverDelegate and implement this delegate method:
func popoverDidClose(_ notification: Notification)
{
statusItem.button?.highlight(false)
}
Outcome
With all of that in place, the button highlighting now works just as it does for Apple's system status bar items like Control Center, Wifi, Battery, etc.
Note: you'll also need to add a global event monitor to listen for clicks that happen outside of your popover to ensure that it closes properly when the user clicks away from it. But that's outside the scope of this question and available elsewhere on SO.
I am trying to create an application that starts with a Status Bar icon, and without a application window. It is for a macOS utility that will be invoked from menus displayed from the Status Bar icon.
Can this be done entirely within SwiftUI, or must I use AppKit?
This is what it looks like in AppKit / Cocoa
theItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
let theStatusButton = theItem!.button
theStatusButton?.title = "MyApp"
let menuItemOne = NSMenuItem(title: "Hey MyApp", action: nil, keyEquivalent: "")
statusMenu = NSMenu(title: "This to do in MyApp")
statusMenu!.addItem(menuItemOne)
theItem!.menu = statusMenu!
Thanks in advance for any help.
macOS 13.0+, Xcode 14.0+
Use SwiftUI's MenuBarExtra structure to create a Status Bar icon and menu on macOS Ventura.
struct MenuBarExtra<Label, Content> where Label : View, Content : View
Look at my post for details.
menuBarIconMenu.popUp(positioning: menuBarIconMenu.item(at: 0), at: NSPoint(x: 1842, y: 1414), in: nil)
i use this code to make the menu open and yet it wont be behind the menu bar like all the other menus any way to have it go behind the menubar?
update1:
statusItem = NSStatusBar.system.statusItem(withLength:NSStatusItem.squareLength)
menuBarMenuIcon = (statusItem?.button)!; menuBarMenuIcon.image = NSImage(named: "MenuBarButton"); menuBarMenuIcon.action = #selector(menuBarMenuClicked); menuBarMenuIcon.sendAction(on: [.leftMouseUp,.rightMouseUp])
this is how i assign the menu
update 2:
this worked
statusItem = NSStatusBar.system.statusItem(withLength:NSStatusItem.squareLength)
statusItem?.menu = menuBarIconMenu
In a previous version of one of my apps, I also opened the menu manually by calling the popup function and experience similiar problems. How did you assign the NSMenu? I would suggest that you assign your NSMenu to the menu property of the NSStatusItem. Then you do not have to add code manually to open the menu. DO you have a custom NSView instance assigned to your NSStatusItem?
private let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
func applicationDidFinishLaunching(_ aNotification: Notification)
{
self.statusItem.menu = self.createMenu()
}
private func createMenu() -> NSMenu
{
// Close
let menu = NSMenu()
menu.addItem(NSMenuItem(title: "Quit", action: #selector(self.quit), keyEquivalent: "q"))
return menu
}
I have a custom image button. I want to display a custom menu when clicked on it.
I am using
settingMenu.popUpMenuPositioningItem(settingMenu.itemAtIndex(0), atLocation: NSEvent.mouseLocation(), inView: nil )
I created a menu and created an outlet for it. Still I am not able to see the menu
Any Suggestions?
In AppDelegate.swift:
let statusItem = NSStatusBar.systemStatusBar().statusItemWithLength(-2)
if let button = statusItem.button {
button.image = NSImage(named: "ButtonImageHere")
button.action = Selector("actionForClickingButtonHere:")
}
func actionForClickingButtonHere(sender: AnyObject) {
//Present view, show menu list, whatever
}
If you want to hide the dock icon do this in Info.plist:
For full example see this tutorial.