I'm using the code below to show the status bar menu on the right click and show the application on the left click.
To achieve that, we need to detach the menu (statusBarItem.menu = nil) once it is closed. Otherwise, we would stop receiving statusBarButtonDidClick event and left-click would not work.
However, when I attach an external screen after right-click menu is getting closed immediately. The problem is in the line: statusBarItem.button?.performClick(nil), but without that menu would not appear instantly.
My guess is that performClick is trying to click on another screen which closes the menu on the active screen.
Is there any way to fix it or achieve the same functionality without performClick?
func createStatusBarItem() {
statusBarItem = NSStatusBar.system.statusItem(withLength: CGFloat(NSStatusItem.squareLength))
if let button = statusBarItem.button {
button.image = Asset.Icons.statusbar.image
button.image?.size = CGSize(width: 16, height: 16)
button.target = self
button.action = #selector(statusBarButtonDidClick)
}
statusBarItem.button?.sendAction(on: [.leftMouseUp, .rightMouseUp])
}
func menuDidClose(_ menu: NSMenu) {
statusBarItem.menu = nil
}
#objc private func statusBarButtonDidClick(sender: NSStatusBarButton) {
let event = NSApp.currentEvent!
if event.type == NSEvent.EventType.rightMouseUp {
let statusBarMenu = StatusBarMenu()
statusBarMenu.delegate = self
statusBarItem.menu = statusBarMenu
statusBarItem.button?.performClick(nil)
} else {
application.showApplication()
}
}
Related
I am having a no-show menuController and I have checked all of the suggestions in previous questions. It turns out the imageView I have implemented a UILongPressGestureRecognizer on, to show the menu, is returning False on calling .becomeFirstResponder just before setting up the menu controller.
I am coding in swift 4 and can't figure out how to make the imageView return True to calling .becomeFirstResponder. Help!
/*********************************************************/
override func viewDidLoad() {
super.viewDidLoad()
// long tap to show menu that enables deletion of the image.
imageView_1.isUserInteractionEnabled = true
let longPressRecogniser = UILongPressGestureRecognizer(target: self, action: #selector(longPressOnImage(_:)))
//longPressRecogniser.numberOfTapsRequired = 1
//longPressRecogniser.numberOfTouchesRequired = 1
longPressRecogniser.minimumPressDuration = 0.5
imageView_1.addGestureRecognizer(longPressRecogniser)
imageView_1.image = placeHolderImage_1
imageView_2.image = placeHolderImage_2
}
/*********************************************************/
#IBAction func longPressOnImage(_ gestureRecognizer: UILongPressGestureRecognizer) {
print(#function)
if gestureRecognizer.state == .began {
//print("gestureRecognizer.state == .began")
self.tappedView = gestureRecognizer.view!
if tappedView.canResignFirstResponder {
print("can resign first responder")
}
if tappedView.becomeFirstResponder() {
print("returned TRUE to becomeFirstResponder")
} else {
print("returned FALSE to becomeFirstResponder")
}
// Configure the shared menu controller
let menuController = UIMenuController.shared
// Configure the menu item to display
// Create a "delete" menu item
let deleteImage = UIMenuItem(title: "Delete", action: #selector(deleteImage_1))
menuController.menuItems = [deleteImage]
// Set the location of the menu in the view.
let location = gestureRecognizer.location(in: tappedView)
print("location = ", location)
let menuLocation = CGRect(x: location.x, y: location.y, width: 2, height: 2)
menuController.setTargetRect(menuLocation, in: tappedView)
//update the menu settings to force it to display my custom items
menuController.update()
// Show the menu.
menuController.setMenuVisible(true, animated: true)
print("menu should be visible now")
}
}
/*********************************************************/
#objc func deleteImage_1() {
print(#function)
}
My caveman debugging print statements output:
longPressOnImage
can resign first responder
returned FALSE to becomeFirstResponder
location = (207.0, 82.0)
menu should be visible now
Create a custom imageView class and override "canBecomeFirstResponder" property like this:
class ResponsiveImage : UIImageView{
override var canBecomeFirstResponder: Bool{
return true
}
}
Use this ResponsiveImage type and your code will work :)
Thank you to adri. Your answer is the solution to my problem.
I had read in other posts to similar questions about overriding var canBecomeFirstResponder but either overlooked it or it wasn't made explicit that a custom UIImageView class needs to be created.
Just to make it clear to newbies like me, the class of the imageView in storyBoard and its #IBOutlet in its viewController must typed as ResponsiveImage. If only one of these is changed a type casting error is reported.
Many thanks for ending my hours of frustration! :-)
I have problem to place grey popUp right on top of screen.
it has x and y = 0.0
override func viewDidLoad() {
let dialogheigth:CGFloat = self.view.frame.height;
let dialogwidth:CGFloat = self.view.frame.width;
self.preferredContentSize = CGSizeMake(dialogwidth,dialogheigth);
}
Guys i use https://github.com/daisuke310vvv/PopupController library as PopUp
my solution was send the right ViewController to PopUp
let popup = PopupController.create(YourViewController).didShowHandler { popup in
// Do something
}
.didCloseHandler { _ in
// Do something
}
The button attached to NSStatusItem is not sending calls to its target selector.
I'm not sure what exactly I am doing wrong. I believe its something to do with the button.action line.
let statusItem = NSStatusBar.system.statusItem(withLength:NSStatusItem.squareLength)
let popover = NSPopover()
func applicationDidFinishLaunching(_ aNotification: Notification) {
if let button = statusItem.button{
button.image = NSImage(named:NSImage.Name("StatusBar"))
button.action = #selector(someAction(_:)) //need help here
}
popover.contentViewController = QuotesViewController.freshController()
constructMenu();
}
#objc func someAction(_ sender: Any?){
print("working");
}
i was following this tutorial - raywenderlich
Edit
This process of setting actions workes for all of the NSMenuItems that I have. I just still the issue with the NSStatusItem.
Edit 2
The code displayed I believe is correct, I now think there's another problem somewhere else.
Edit 3 Answer
"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."- appledev NSStatusItem.action
"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
You can dynamically assign and show the menu property to the button as needed. You'll need to implement the NSMenu menuDidClose method
self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength: NSVariableStatusItemLength];
[self.statusItem.button sendActionOn: (NSLeftMouseUpMask|NSRightMouseUpMask)];
[self.statusItem.button setAction: #selector(statusItemClicked:)];
-(IBAction) statusItemClicked: (id) sender
{
NSEvent *currentEvent = [NSApp currentEvent];
// Right click conditions
if ((([currentEvent modifierFlags] & NSControlKeyMask) == NSControlKeyMask) ||
(([currentEvent modifierFlags] & NSCommandKeyMask) == NSCommandKeyMask) ||
(([currentEvent modifierFlags] & NSRightMouseUpMask) == NSRightMouseUpMask) ||
(([currentEvent modifierFlags] & NSRightMouseDownMask) == NSRightMouseDownMask) ||
([currentEvent type] == NSRightMouseDown) ||
([currentEvent type] == NSRightMouseUp))
{
[self showStatusMenu: self];
}
else
{
// do something besides showing the menu
}
}
-(IBAction) showStatusMenu: (id) sender
{
self.statusItem.menu = self.statusMenu;
[self.statusItem.button performClick:nil];
}
-(void) menuDidClose : (NSMenu *) someMenu
{
if (someMenu == self.statusMenu)
{
// re-configure statusitem
self.statusItem.menu = nil;
[self.statusItem.button sendActionOn: (NSLeftMouseUpMask|NSRightMouseUpMask)];
[self.statusItem.button setAction: #selector(statusItemClicked:)];
}
}
I created a NSStatusBarItem and popUp a (programmatically generated) NSMenu on right click:
let statusBarItem = NSStatusBar.system().statusItem(withLength: -1)
statusBarItem.action = #selector(AppDelegate.statusBarItemAction(sender:))
let menu = NSMenu()
var menuItem = NSMenuItem()
menuItem.action = #selector(AppDelegate.customItemAction)
menu.addItem(menuItem)
func statusBarItemAction(sender: NSStatusItem) {
let mouseEvent = NSEvent.pressedMouseButtons()
if mouseEvent == 2 {
// right click
lxStatusBarItem.popUpMenu(menu)
}
}
func customItemAction() {
// do something
}
Everything works fine, except that the statusBarItem remains highlighted after customItemAction is called:
How can I solve this?
I found that setting statusItem.button?.isHighlighted = false helped with removing the highlight. In your case, this would look like this:
func customItemAction() {
// do something
statusBarItem.button?.isHighlighted = false
...
}
I'm building a NSStatusBar app and want to call different functions depending on if the user clicked left- or right on the icon.
Here is what I have so far:
let statusItem = NSStatusBar.systemStatusBar().statusItemWithLength(-1)
func applicationDidFinishLaunching(aNotification: NSNotification) {
let icon = NSImage(named: "statusIcon")
icon?.setTemplate(true)
statusItem.image = icon
statusItem.menu = statusMenu
}
With this it shows up the statusMenu by every click. How can I distinguish the mouseEvents?
This snippet
func menuSelected (sender:AnyObject) {
var clickMask: Int = 0
if let clickEvent = NSApp.currentEvent! { // see what caused calling of the menu action
// modifierFlags contains a number with bits set for various modifier keys
// ControlKeyMask is the enum for the Ctrl key
// use logical and with the raw values to find if the bit is set in modifierFlags
clickMask = Int(clickEvent.modifierFlags.rawValue) & Int(NSEventModifierFlags.ControlKeyMask.rawValue)
}
if clickMask != 0 { ... } // click with Ctrl pressed
}
from my code checks for a ctrl-click. It's place in the method that is called from a menu item like this:
let item = NSMenuItem(title: title, action: Selector("menuSelected:"), keyEquivalent: "")