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.
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 developing a macOS app (using Swift & Storyboard) which window behaves like the Adobe Creative Cloud app. And I could not find the optimal solution after hours of research.
This means:
When the app launches, the main window shows up with various menus on the status bar, an icon appears in the dock, and an icon appears in the status bar.
When the user clicks the red X, the main window and the icon in the dock are hidden.
The main app window can be reopened by clicking the status bar icon. And the dock icon reappears.
My storyboard looks like this:
I have tried the following:
By setting Application is agent (UIElement) to YES, I was able to close the main app window while keeping the app alive. However, the app icon does not show up in the dock, and there are no menus in the left side of the status bar.
I was able to launch a new app window by clicking the status bar icon. But doing so simply opens a whole new window regardless of whether a window is already being presented (I only want one window to show up).
let storyboard = NSStoryboard(name: "Main", bundle: nil)
guard let window = storyboard.instantiateController(withIdentifier: .init(stringLiteral: "main")) as? WindowController else { return }
window.showWindow(self)
Much appreciation for anyone who can help!
Don't use the Application is agent approach, but change the activationPolicy of the NSApp.
To dynamically hide the icon after closing the (last) window use this in your AppDelegate:
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
NSApp.setActivationPolicy(.accessory)
return false
}
And use something simular to this to initialise your menubar icon and activate the window including a dock icon:
class ViewController: NSViewController {
var status: NSStatusItem?
override func viewDidLoad() {
super.viewDidLoad()
status = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
status?.button?.title = "Test"
status?.button?.action = #selector(activateWindow(_:))
status?.button?.target = self
}
#IBAction func activateWindow(_ sender: AnyObject) {
NSApp.setActivationPolicy(.regular)
DispatchQueue.main.async {
NSApp.windows.first?.orderFrontRegardless()
}
}
}
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
Hi I am looking to make a pop out table view similar to the one below and with selectable cells. Any help would be great thanks!
Here is what I would like to style it after
You will want to make use of UIPopoverPresentationController.
Make a tableviewController with the data you want to be able to select
Make a button that will send the action so you can present the popover
Do something with the item selected from the popover.
The second item above looks something like this (my own code example):
#IBAction func shareAction(_ sender: UIButton) {
let controller = ShareVC()
controller.socialPost = self.item
controller.modalPresentationStyle = .popover
// configure the Popover presentation controller
let popController: UIPopoverPresentationController? = controller.popoverPresentationController
popController?.permittedArrowDirections = [.up, .down]
popController?.delegate = self
// in case we don't have a bar button as reference
popController?.sourceView = sender
popController?.sourceRect = sender.frame
popController?.backgroundColor = .white
self.parentViewController?.present(controller, animated: true, completion: { })
}
In my case the 'ShareVC" is a UITableViewController with item sharing options. Pretty similar to your screenshot as far as layout goes.
This will present that popover from the button that was tapped. Then again, you just need to handle the selection of the items from that ViewController.