I'm having difficulty getting my NSPopover to function properly. The statusItem pops up in the status bar, and highlights on click, but the popover isn't displaying.
Here's the structure of my code.
#property (strong, nonatomic) NSStatusItem *statusItem;
#property (strong, nonatomic) NSEvent *popoverTransiencyMonitor;
#property (weak, nonatomic) IBOutlet NSPopover *popover;
#property (weak, nonatomic) IBOutlet NSView *popoverView;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
self.statusItem.highlightMode = YES;
[self.statusItem.image setTemplate:YES];
self.statusItem.action = #selector(itemClicked:);
}
-(void)itemClicked:(id)sender {
[[self popover] showRelativeToRect:[sender bounds] ofView:sender preferredEdge:NSMinYEdge];
if (self.popoverTransiencyMonitor == nil) {
self.popoverTransiencyMonitor = [NSEvent addGlobalMonitorForEventsMatchingMask:(NSLeftMouseDownMask | NSRightMouseDownMask | NSKeyUpMask) handler:^(NSEvent* event) {
[NSEvent removeMonitor:self.popoverTransiencyMonitor];
self.popoverTransiencyMonitor = nil;
[self.popover close];
}];
}
}
I got help from another person on StackOverflow, Gavin, and that help is located here. NSPopover transiency when popover is in status bar
I reached out to Gavin, and he managed to help me out by sending me his xCode project and it gave me some insight. But our code matches, mine doesn't work, and his does.
Any ideas?
This is a bit embarrassing, but it seems that the issue was that I created a project which uses Storyboards, and NSPopover wouldn't work with that for some reason.
Sorry for the clutter.
Related
I have an array of UIButton objects (I have 15 buttons so I wanted to store them in an array for easy processing). I want to be able to iterate through the array and change the background image/image that the button uses. This is how I currently have it set up:
#property (nonatomic, weak) UIImage *greenLight;
#property (nonatomic, weak) UIImage *yellowLight;
#property (nonatomic, weak) UIImage *redLight;
#property (weak, nonatomic) IBOutlet UIButton *task1Button;
#property (weak, nonatomic) IBOutlet UIButton *task2Button;
#property (weak, nonatomic) IBOutlet UIButton *task3Button;
#property (nonatomic, copy) NSMutableArray *buttonArray;
- (instancetype)init
{
self = [super init];
if(self){
[self.buttonArray addObject:self.task1Button];
[self.buttonArray addObject:self.task2Button];
[self.buttonArray addObject:self.task3Button];
self.greenLight = [UIImage imageNamed:#"greenlight.ICO"];
self.yellowLight = [UIImage imageNamed:#"yellowlight.ICO"];
self.redLight = [UIImage imageNamed:#"redlight.ICO"];
NSLog(#"init complete...");
}
return self;
}
Then I have the following in viewWillAppear
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.task1Button setImage:self.redLight forState:UIControlStateNormal];
[(UIButton *)self.buttonArray[1] setImage:self.greenLight forState:UIControlStateNormal];
}
The second line in viewWillAppear works and changes the button's image. The third line, however, does not. The debugger shows the code reaching that line and not throwing any errors, so I don't know why setting the button image when the button is in an array would not work.
Any ideas?
Try replacing last line in your answer with this :
[(UIButton *)[self.buttonArray objectAtIndex:1] setImage:self.greenLight forState:UIControlStateNormal];
But i'm doing this without compiler so i'm not really sure about where to place the first bracket [
Tell me if it works, i'm not totally sure about it.
I'm a very beginner in Objective-C and in Mac development. I've searched this site for the answer I'm seeking, but due to a big ignorance I could not understand how this should be done.
The following piece of code implements a menu in Mac status bar, near the battery, wifi, etc icons on the top right of the screen.
If the user disconnects a device from the computer the method sayAdapterIsConnected is called with one boolean argument. Depending on that argument, one of the menu items, in this case the enabledOption, should get disabled. I mean, the menu item should be enabled or disabled.
This works only if the menu is closed. If the statusMenu is opened, it is not updated.
I know there is a way to update an open statusItem menu. Wifi status icon updates its networks while the menu is open.
#interface ENHeringAppDelegate : NSObject <NSApplicationDelegate> {
NSStatusItem * statusItem;
}
#property (weak) IBOutlet NSMenu *statusMenu;
#property (weak) IBOutlet NSMenuItem *enabledOption;
#end
#implementation ENHeringAppDelegate
- (void) awakeFromNib {
statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
[statusItem setMenu:_statusMenu];
[statusItem setTitle:#"TEST"];
[statusItem setHighlightMode:YES];
}
- (void)sayAdapterIsConnected:(bool)connected {
if (connected)
[self.enabledOption setEnabled:true];
else
[self.enabledOption setEnabled:false];
}
#end
A NSMenu isn't needed here. You also don't need the setMenu: call.
Here's a working example:
AppDelegate.h
#import <Cocoa/Cocoa.h>
#interface KBCAppDelegate : NSObject <NSApplicationDelegate> {
NSStatusItem *statusItem;
}
#property (assign) IBOutlet NSWindow *window;
- (IBAction)update:(id)sender;
#end
AppDelegate.m
#import "AppDelegate.h"
#implementation KBCAppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
[statusItem setTitle:#"TEST"];
[statusItem setHighlightMode:YES];
[statusItem setEnabled:YES];
[statusItem setTarget:self];
}
- (IBAction)update:(id)sender {
if (!statusItem.isEnabled) {
[statusItem setEnabled:true];
} else {
[statusItem setEnabled:false];
}
}
#end
That's all you need to do. (I made update: an IBAction, but a void function like what you show would work too)
P.S.
Make us nitpickers happy and always use braces in your if/then statements, even if there is only one line. :)
I'm building a camera application, and I can't get the zooming on the image to work. I honestly don't know where to go from here, I'm sure it's some dumb mistake that I'm missing though. The app sets the chosenImageView to the workingImage (picture from the camera). When I try to zoom, nothing happens.
ViewController.h
#interface ViewController : UIViewController <UIActionSheetDelegate, UIImagePickerControllerDelegate, UIScrollViewDelegate>
#property (strong, nonatomic) UIImage *workingImage;
#property (weak, nonatomic) IBOutlet UIImageView *chosenImageView;
#property (weak, nonatomic) IBOutlet UIScrollView *imageScroller;
-(IBAction)cameraButtonPressed;
#end
ViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.imageScroller.minimumZoomScale = 0.01f;
self.imageScroller.maximumZoomScale = 6.0f;
self.imageScroller.contentSize = self.imageScroller.frame.size;
self.imageScroller.scrollEnabled = YES;
self.navigationController.navigationBar.tintColor = [UIColor blackColor];
}
-(UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return self.chosenImageView;
}
Here was the problem. You have to control+drag from the UIScrollView to the View Controller to set the delegate.
Im surprised that this hasn't already been asked:
But how does one go about changing the NSMenuItem title in a NSStatusBar menu. When a user logs in I want the menu item to say logout. I have tried creating an outlet to modify my NSMenuItem as a would a label or something.
AppDelegate.h
#property (retain) IBOutlet NSMenuItem *loginItem;
AppDelegate.m
[loginItem setTitle:#"Logout"];
But that didnt work.
The only thing that I was able to do was delete the old NSMenuItem, then add a new one, but it would just add it to the bottom. Is the only way to do this to remove every menu item then re-add them?? That seems very inefficient.
The method you describe should work, though, in general, keeping IBOutlets for all your menu items can be tedious. (If your solution isn't working, make sure the IBOutlet is actually connected in the nib file, and make sure that you're setting the title at an appropriate time. If you're trying to set it in your controller's init method, for example, that's too early on, and the outlets haven't yet been connected up: move the method to awakeFromNib or similar.
A better approach in the long run is to use the <NSMenuDelegate> protocol and NSMenuValidation (informal) protocol to update menu items dynamically (and lazily).
For example, define your controller class like the following:
#interface MDAppDelegate : NSObject <NSApplicationDelegate, NSMenuDelegate>
#property (strong) NSStatusItem *statusItem;
#property (weak) IBOutlet NSWindow *window;
#property (weak) IBOutlet NSMenu *statusItemMenu;
#property (weak) IBOutlet NSMenuItem *toggleLoginLogoutMenuItem;
#property (weak) IBOutlet NSTextField *statusField;
#property (weak) IBOutlet NSTextField *progressField;
#property (weak) IBOutlet NSProgressIndicator *progressIndicator;
#property (assign) BOOL loggedIn;
- (IBAction)toggleLoginLogout:(id)sender;
#end
In the nib file, the delegate outlet of the statusItemMenu is set to the MDAppDelegate controller class. That assures that the MDAppDelegate class is in the responder chain and allow it to work with validating the menu items.
Then you could implement your .m like the following:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
_statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
_statusItem.menu = _statusItemMenu;
_statusItem.title = NSLocalizedString(#"NSStatusItem", #"");
[self updateLoggedInStatus];
}
- (void)updateLoggedInStatus {
[self.statusField setStringValue:(self.loggedIn ? #"Logged in" : #"Logged out")];
}
- (IBAction)toggleLoginLogout:(id)sender {
[self performSelector:#selector(finishFakeLoginLogout:)
withObject:nil afterDelay:2.0];
}
- (void)finishFakeLoginLogout:(id)sender {
self.loggedIn = !self.loggedIn;
[self updateLoggedInStatus];
}
- (void)menuNeedsUpdate:(NSMenu *)menu {
#if MD_DEBUG
NSLog(#"[%# %#]", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
#endif
}
- (BOOL)validateMenuItem:(NSMenuItem *)menuItem {
#if MD_DEBUG
NSLog(#"[%# %#]", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
#endif
SEL action = menuItem.action;
if (action == #selector(toggleLoginLogout:)) {
[menuItem setTitle:(self.loggedIn ? #"Logout" :#"Login")];
}
return YES;
}
Sample project: http://github.com/NSGod/NSStatusBarFinagler
You don't need to connect your menu item just try this..
NSMenuItem *menuItem = (NSMenuItem*) sender;
NSString *menuString = menuItem.title;
if ([menuString isEqualToString:#"Login"])
{
[menuItem setTitle:#"LogOut"];
}
NSMenuItem menuItem = (NSMenuItem) sender;
this line automatically collect the menu items in your app.
i'm trying to code a drag&drop menubar app. i used a custom view to access to the dropped file, and this works fine. now i would like to open the default menu when clicking on this view. i'm using this:
- (void)mouseDown:(NSEvent *)event {
[statusItem popUpStatusItemMenu:statusMenu];
}
now, the mouseDown works fine (trying with NSLog), but still i cannot access to statusItem and statusMenu.
this is in dropView.m, in dropView.h i got:
#interface dropView : NSView{
IBOutlet NSMenu *statusMenu;
NSStatusItem *statusItem;
}
no crash, no logsā¦ any ideas? ty!
this is a bit more from the .m
- (void)awakeFromNib{
statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
[statusItem setMenu:statusMenu];
dropView *drop = [[dropView alloc] initWithFrame:NSMakeRect(0, 0, 24, 24)];
[statusItem setView:drop];
}
Add this to your .h file:
#property (strong, nonatomic) IBOutlet NSMenu *statusMenu;
#property (strong, nonatomic) NSStatusItem *statusItem;
Add this to your .m file:
#synthesize statusMenu, statusItem;
Then you'll access the properties with self.statusMenu and self.statusItem.