objective-cnsstatusitemnspopoverawakefromnib

Need to make NSPopover appear during awakeFromNib?


I have a statusitem in the system menu bar that makes a popover appear when it is clicked. I would like to make the popover automatically appear when the application is first launched. I tried to add the [self clickStatusBar:self] to the awakeFromNib method but it doesn't work. Anyone know how to accomplish this?

Here are my current methods:

- (void)awakeFromNib {
    statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
    [statusItem setTitle:@"Locating..."];
    [statusItem setTarget:self];
    [statusItem setAction:@selector(clickStatusBar:)];
    [statusItem setHighlightMode:YES];
}

- (void)clickStatusBar:(id)sender {
    [[self popover] showRelativeToRect:[sender bounds] ofView:sender preferredEdge:NSMinYEdge];
}

I tried adding applicationDidFinishLaunching: as

- (void)applicationDidFinishLaunching:(NSNotification *)notification {
    [self clickStatusBar:self];
}

but I get an error of -[AppDelegate bounds]: unrecognized selector sent to instance


Solution

  • In awakeFromNib: the application is not fully launched yet, only this NIB file has been unmarshalled. At this point this method gives you a hook to complete (object-local) initialization. The application is (quite likely) not ready to process events or perform actions.

    You should trigger that action from applicationDidFinishLaunching: method, an optional method in the application delegate, and pass in the status item like a click would do (because you query it for bounds).

    Update. This is trickier than I had thought. Turns out, the NSStatusItem doesn't have a view associated at that point when the delegate gets called. I'd venture to say that is a bug with NSStatusItem. When statusItem.view is called in applicationDidFinishLaunching:, the popover method receives nil and complains.

    The only (partial, see below) workaround that I have found is to set a button as view manually in awakeFromNib: like so:

    - (void)awakeFromNib
    {
      self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
    
      self.statusItemButton = [[NSButton alloc] initWithFrame:NSMakeRect(0, 0, 83, 22)];
      self.statusItemButton.title = @"Locating...";
      self.statusItemButton.bordered = NO;
      [self.statusItemButton setAction:@selector(clickStatusBar:)];
    
      self.statusItem.view = self.statusItemButton;
    }
    

    That way you'd have a view around when the application is done launching. But beware, it doesn't look like the default one.

    PS. And curiously, and does not even work every time. I have to drop the ball here. Sorry. Maybe save the location in the defaults when somone clicks. I think I saw such an inconsistency in Cloud.app and with a popover next to a status item, and maybe now we know why :)