Search code examples
macoscocoansviewnspopover

NSView inside an NSPopover: Converting Coordinates


Context

Let P be an NSPopover and view be an NSView that is a subview contained within P.

What I Need

I want to take the frame of view and convert it to the screen's coordinate system. Normally, you would do that like this:

NSRect frameRelativeToWindow = [self convertRect:self.frame relativeToView:nil]; 
[self.window convertRectToScreen:frameRelativeToWindow];

However, because view is in an NSPopover, its window property is nil and the above method will return NSZeroRect: [0, 0, 0, 0]

So, how can I get my view's frame in screen coordinates if the view is in an NSPopover?

Notes:

  1. The NSPopover IS onscreen and visible.
  2. The view whose frame I am trying to convert to screen coordinates is not a direct subview of the NSPopover or its contentView. The view in question is actually a custom NSView within an NSTableCellView within an NSTableView within an NSScrollView within an NSClipView. The clipView is a direct subview of the NSPopover's contentView.
  3. In a normal NSWindow, you can take any subview and call [self window] to get the containing NSWindow object. Doing that in a popover, however, returns nil.

Solution

  • The following code assumes, that you have

    • a NSViewController subclass that manages the popover view
    • the view controller implements NSPopoverDelegate

    As hamstergene pointed out, the popover's window won't be available until it is shown.
    When the popoverWillShow: delegate method gets called, your popover view's window should be an instance of _NSPopoverWindow (and not nil).

    - (void)popoverWillShow:(NSNotification *)notification
    {
        NSWindow* popOverWindow = self.view.window;
        NSRect popoverRectInScreenCoords = [popOverWindow convertRectToScreen:self.view.frame];
        NSLog(@"Popover Rect in Screen Coords:%@", NSStringFromRect(popoverRectInScreenCoords));
    }
    

    Update
    Bryan (the OP) posted a solution to his problem in the comments.
    The issue wasn't NSPopover itself. The actual reason for the nil window for some of his subviews was NSTableView, which releases non-visible NSTableCellViews.
    As a workaround he implemented viewDidMoveToWindow in his custom NSTableCellView subclass. When viewDidMoveToWindow: gets called, the table cell view already has a window that can be used to perform coordinate conversion.