macoscocoanstableviewnstablecellview

How to get all TableCellView prototypes from a TableView object?


I am doing my own localization, where I pre-parse all window controls after loading a window/view, and translate all specially-marked values, tooltips etc.

If I preset a tooltip for a Table Cell View, though, I cannot figure out how to reach it from the NSTableView object.

Here's the cell view in Xcode's IB:

TableCellView in TableView hierarchy

And the tooltip property of the cell view:

Tooltip field in Property Inspector

How do I get to that TableCellView in its prototype form when I only have a reference to its TableView object, so that I can alter its tooltip for all rows added later?

There must be a way, because how would a TableView know how to add a row when using a NSArrayController to manage the table view rows, and my code does not explicitly reference these prototypes at all, which means that the NSArrayController must have a way to find these cell prototypes – and I want to achieve the same.

I understand that I can add a binding from the header view in the code that loads the view, but I am trying to write a generic method that is only given the root tableview and finds all tooltips inside.


Solution

  • I believe this is not possible with AppKit API.

    Here's why I come to that conclusion, so that in case I'm wrong (or even if I'm right), the reasoning is still valuable:

    Reasons Your Request to Change "the row (cell) prototype" is not possible

    Each New Cell is Created Directly from Nib

    Weak Sign: Inspecting awakeFromNib Usage

    For each NSTableViewRow and NSTableViewCell that is being created, the owner receives an -awakeFromNib: message. The owner is usually your table view controller -- or whatever is the file's owner of the Nib. (To keep it simple, I assume you have a Nib MyTableViewController.xib for a view controller of the same name, with the Nib containing one table view in a container.)

    This is one strong sign (and cause of bugs :)) that each cell you create (not reuse) is created from the Nib.

    Strong Sign: You can see (and change) Nib-to-Cell association

    This is also discussed in literature. From a source on UIKit, though, but similar constraints apply to AppKit Nibs if you want to design cells in separate nibs:

    This nib is expected to have exactly one top-level object, and that top-level object is expected to be a UITableViewCell; that being so, the cell can easily be extracted from the resulting NSArray, as it is the array’s only element. Our nib meets those expectations! Problem solved.
    (Matt Neuburg: Programming iOS 6; Designing a cell in a nib})

    I found that mind-set to be helpful: that the main view controller Nib is, for all intents and purposes, treated as a Nib for the cell in the future.

    It's accessible by the NSUserInterfaceItemIdentifier if you look at registered Nibs: For AppKit, we have NSTableView.registeredNibsByIdentifier to inspect, and NSTableView.register(_:forIdentifier:) to change Nib association per cell.

    So if you want to adjust something, your best bet is to override the Nib association there.

    Programmatic Write Access to Nib internals is not possible

    Nibs are opaque data blobs and have no public surface API. NSNib has no API to modify any of its objects.

    Actual Ways to Solve the Underlying Problem of Changing the Default Value

    Programmatically Influencing of Object Restoring from Nib is possible

    You can't programmatically set the blueprint of the NSNib that's being loaded, but you can hook into the process:

    You can subclass NSNib if you want to extend or specialize nib-loading behavior. For example, you could create a custom NSNib subclass that performs some post-processing on the top-level objects returned from the instantiateNib... methods https://developer.apple.com/documentation/appkit/nsnib

    So when your NSTableViewCell is being instantiated, you can replace the tooltip.

    That means you need a NSNib subclass with a possibly static var tooltip to store the next tooltip value; you can't use the Nib file itself as your storage.

    Using NSTableView Callbacks instead

    The main customization point for NSView-based table views like you have is NSTableViewDelegate.tableView(_:viewFor:row:). This also applies to re-used cells, so you get possibly more consistent behavior no matter whether the cell is newly created before or after the point in time you wanted to modify the tooltip.

    Since you can't use the Nib itself as your tooltip data storage but need to put a global variable, a property on the view controller (associated with the table view's lifetime), or static var on a type like MyCustomNib: NSNib, you might as well use the view controller property and the NSTableViewDelegate callback in a lot of cases.