简体   繁体   中英

Getting position of custom NSTableCellView within NSTableView

I am trying to add an NSPopover to a custom NSTableCellView within an NSTableView. I am able to get the NSPopover to appear corresponding to the correct element, but the position of the popover is incorrect. To make clear my problem in case someone has a better suggestion for design: when a user hovers over a contact in a table of contacts, I want to show more info for that contact in a popover (no clicking required)

Currently the NSPopover is initialized as follows: contactPopover = NSPopover(contentViewController: contactViewController, sender: allContactsViewController.view, bounds: contactTableCellView.frame) where that initializer just lightly sets up the popover behaviors and calls popover.show(...) where contactTableCellView is the table cell view of the contact I want.

The issue is that contactTableCellView.frame always has the (x,y) = (0,0) likely because that's where the template for the custom NSTableCellView starts in the xib. To properly place the popover on the table, I need to get the position of the table cell view relative to the origin of the NSTable.

Right now each NSTableCellView has mouse tracking setup so that on mouse-enter, I notify the contact list to display the popover, where I pass the corresponding mouse-entered table cell view. This gives me the access to the table cell view when trying to get the position relative to the table, but I can't find anything in the documentation for how to get the real position of a table cell view relative to the table origin.

Looked at: https://developer.apple.com/reference/appkit/nstableview https://developer.apple.com/reference/appkit/nstablecellview

I'm not sure what this is about:

contactPopover = NSPopover(contentViewController: contactViewController, sender: allContactsViewController.view, bounds: contactTableCellView.frame)

Where did that method come from? It's not part of NSPopover itself.

Anyway, this should do just what you want, assuming a popover created without extraneous baggage:

contactPopover.show(relativeTo:contactTableCellView.bounds of:contactTableCellView preferredEdge:.minY)

That will position the popover relative to the cell view without you having to figure out where the cell view is within the table view.

Going off of the convo with @KenThomases where the best way would be to have the table view cell itself manage the popover, but that's only half-possible. I'll explain:

Since tables redraw the table cell views, the contactTableCellView can't promise that it exist for the entire lifetime of the NSPopover. In order for the popover to exist with a lifecycle unrelated to the contactTableCellView was to have an NSPopover object exist in my class holding the NSTableView (lets call it addressBookViewController). So addressBookViewController will have a member named contactPopover and as long as the addressBookViewController is still in memory, we can be sure the popover will exist. The problem with this was getting the right bounds for the popover to link to.

If you pass the current contactTableCellView back to the addressBookViewController and just use something like contactPopover.show(relativeTo:contactTableCellView.bounds of:contactTableCellView preferredEdge:.minY) as @KenThomases mentioned, you'll see that the popover gets stuck to the the first/top element in the NSTableView no matter what element I hover over, but I want the popover to point to the element that is hovered over.

I couldn't find any reference material to prove my hypothesis, but from my outcome, it seems that the NSTableView has cells/views that contain the custom ContactTableCellView I created. This is the reason that I was getting "contactTableCellView.frame always has the (x,y) = (0,0)". The frame is always relative to the parent object, which in this case is a view/cell with the same size as the TableCellView. And this outer view had the frame that I wanted/expected contactTableCellView.frame to have. To say this clearly:

contactTableCellView.superView?.frame gave the correct (x,y) in the NSTableView and this frame isn't deleted as contactTableCellView gets redrawn for various reasons. Using my findings, I was able to get a popover appear relative to the correct element like I wanted roughly like this in Swift 3:

class AddressBookViewController {
...
var contactCardPopover: NSPopover?
...

func showContact(sender: AnyObject, contact: ContactVM) {
    guard let contactTableCellView = sender as? ContactTableCellView else { return }

    // when contactCardVC is non-nil, a popover currently open. This makes sure 2 don't open in the same tableView.
    if contactViewController == nil {
        contactCardViewController = ContactViewController(contact: contact)

        // this here is the important part!!!! Using the contactTableCellView.superview
        if let tableCell = participantTableCellView.superview {

            contactCardPopover = NSPopover(contentViewController: contactCardViewController, sender: tableCell, bounds: tableCell.frame)
            contactCardPopover.delegate = self
        }
    }
}

}

So the superview of the ContactTableCellView has the correct frame, so my NSPopover works on the custom NSTableCellView like I originally wanted! It just took a few hours to figure out this specific part.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM