Using the new NSPopover (since Mac OS X 7)

NSPopover in action

NSPopover in action

NSPopover is the animated balloon-like component that shows the downloaded files in Safari 5.1. This was added in Mac OS X Lion. If you ever wondered how to add it to your code, it’s extremely simple.

How it works

The principle is that the popover links a NSView with a certain position in the window. The NSView contains the stuff the user will see in the popover while the position is what the popover will point to. This is a NSView itself, usually a NSButton (like in our example). So we’ve got 2 NSiews: the popover view and the trigger view. Looking at the image on the right, the popover view contains the application icon and the You may put anything here text. The trigger view is the round button called Popover.

How it’s made

First let’s create the main window containing the Popover¬†button. It would be nice to have an on/off button. When the button is pressed, the popover should appear whereas the button is depressed, the popover would, not surprisingly, disappear. Then, completely separate from the main window, we’ll add a NSView and put the image and text inside it. Finally we can add the popover. To do that, in the xib editor we search for NSPopover. An object named Popover and View Controller will come up. We need to drag it into the objects list in the xib.

Popover XIB

Popover XIB

The popover is already connected with the controller, so we only need to connect the controller’s view property with our view. That’s the actual trick, this is how the popover knows to display the custom view when it’s shown.

Connection popover controller with view

Connecting popover controller with view

And finally we are left with implementing the button action. This may look like this:


- (IBAction)togglePopover:(id)sender
{
    if (self.buttonIsPressed) {
        [self.popover showRelativeToRect:[popoverTriggerButton bounds]
                                  ofView:popoverTriggerButton
                           preferredEdge:NSMaxYEdge];
    } else {
        [self.popover close];
    }
}

To determine is the button is pressed or not, look for the button’s value.

- (BOOL)buttonIsPressed
{
    return self.popoverTriggerButton.intValue == 1;
}

Detach the popover to a window

This is a pretty cool effect. Users can drag the popover and this would create a nice transition to a window. For this, a method needs to be implemented in the popover’s delegate (NSPopoverDelegate):

- (NSWindow *)detachableWindowForPopover:(NSPopover *)popover
{
    return self.popoverWindow;
}

The window can have different contents than the popover. Actually it must contain other components and if we wanted to show the same things as the popover we’d need to copy those from the popover. In our example, we changed the label text slightly.

Popover-Window Transition

Popover-Window Transition

Sources

To help you getting started with NSPopover, you can download the complete source code for the example application described in this post:

PopoverExample.xlsx (change extension from xlsx to zip after download, wordpress restrictions).

3 thoughts on “Using the new NSPopover (since Mac OS X 7)

  1. Ram

    Cool, very simple, thanks for the info.

    Any idea if I can make a StatusItem show a NSPopover/NSView instead of a NSMenu? and how I would call

    showRelativeToRect:[popoverTriggerButton bounds] ofView:popoverTriggerButton preferredEdge:NSMaxYEdge

Comments are closed.