How to bind different entities with NSTableView and NSTabView? - objective-c

I have an NSArrayController which handles entities of GeometryShape.
GeometryShape has: name, type, color.
LineShape is a GeometryShape and has: beginPositionX, beginPositionY, endPositionX, endPositionY.
CircleShape is a GeometryShape and has: positionX, positionY, radius.
The NSTableView shows all inserted shapes in the NSArrayController, where each column is bound with arrangedObjects & the key name.
When I select a line shape, its properties are displayed in the Line tab - which is the default tab.
Now if I select a circle shape, I want the Circle tab to be selected and the circles properties to be displayed.
…and so depending on which shape type I select the corresponding tab will be selected and display the corresponding shape properties.
How may I achieve this excellent :) model?

I think you'd want to implement a NSTableViewDelegate and programmatically select the appropriate tab within an implementation of tableViewSelectionDidChange: When the selection changes, you just grab the tabView's IBOutlet and assign a new selectedIndex based on the arrayController's selection.
Alternately, you could bind the value of the tabView's selectedIndex to the array controller's selection, but you would need a custom value transformer that converted from the selection id to an NSUInteger that reflects the appropriate class.
In either implementation, you're writing code using isKindOfClass and mapping to an integer.
You may also be able to bind the tab view's selectedLabel to the array controller keypath of selection.class but I'm guessing you still would need a valuetransformer wrapping NSStringFromClass() as described in the NSValueTransformer docs. I'm not totally certain there's a completely non-code way of transforming the class to a string you could bind the selectedLabel to, though.
Personally, I don't love implementing the custom value transformer because you're writing code to allow implementing behavior buried in IB... all to avoid writing code that could live in a custom tableview delegate.

Related

How do I determine the row in NSTableView that represents a specific object instance?

I have a NSTableView driven by NSManagedObject/CoreData and a custom graph view on the same form which plots each row on a X/Y grid based on some derived calculations. Each point on the graph view causes and event to run a (SEL)Selector on my window controller..
And I am stuck ! I want to be able to change the selection in the NSTableView to be the object selected and returned by the graph view. ie. use the graph to change the selection/row in the NSTableView list.
I have a method as follows:
-(void)setSelectedOpportunity:(Opportunity *)opty
{
_selectedOpportunity = opty;
[_opportunityTable selectRowIndexes:???? byExtendingSelection:NO];
}
_opportunityTable is an NSTableView that gets is content from and NSArrayController called _opportunityAC. This array controller gets its data from a NSManagedObjectContext containing NSManaged objects called Opportunity.
Can anyone tell me how do I determine the row in the table that represents the object opty.
Thanks
Ian

How can an NSCell detect the model key path it's bound to?

Imagine the following:
You have an NSTableView with multiple columns.
Each NSTableColumn is bound to the same NSArrayController (we'll call it myArrayController).
This array controller holds many instances of a model class.
One column has an NSPopupButtonCell where selectedObject is bound to myArrayController.arrangedObject.somePropertyOfTheModel.
The table gets properly populated.
Q: How can an NSCell detect the model key path it's bound to? (somePropertyOfTheModel in this example)
I'm trying to make a cell reusable by not having it assume its represented value is always from somePropertyOfTheModel (could be from somethingElse). Upon a given action, it needs to bind the content of a 2nd controller to somePropertyOfTheModel or somethingElse.
[Edited] A bit more (maybe too much?) explanation:
I'm creating a popup-button which displays a few preset values of a property, and a "Custom Value" item which triggers a PopOver window to allows configuration of the property. I want to make it so that I can drop this cell into a table and having it manage the PopOver much like it already manages its own Menu.
What I've tried:
[self representedObject] returns the actual value. Setting it as content to the 2nd controller is all good and well... but whenever the model's property changes, the 2nd controller won't be notified since it's tied to the actual instance of the value... not a binding to the model property.
Querying the cell's binding gives me nothing:
[[self infoForBinding:#"selectedObject"] objectForKey:NSObservedObjectKey]; // nil returned
[[self infoForBinding:#"selectedObject"] objectForKey:NSObservedKeyPathKey]; // nil returned
Querying the cell's control's (the NSTableView) binding doesn't give me much:
[(NSTableView*)[self controlView] infoForBinding:#"content"] objectForKey:NSObservedObjectKey]; // returns myArrayController or a poxy to it.
[(NSTableView*)[self controlView] infoForBinding:#"content"] objectForKey:NSObservedKeyPathKey]; // returns #"arrangedObject"
// running the same but for #"selectedObject" returns nothing but nils
I'd like to query the NSTableColumn itself -- that's where the bindings are defined in IB -- but cell's aren't aware of their existence (unless I've overlooked something obvious). Even passing via NSTableView, no method returns an NSTableColumn for a given cell (and considering prototype cells, I doubt it would help).

Binding columns in an NSTableView

I have two classes: GHTable and GHColumn. A GHTable object has an NSMutableArray with GHColumn objects. Each GHColumn has a name property (NSString).
I have made an UML diagram to make this more clear. Note that I am not using Core Data:
I want to bind the columns property of the GHTable object to the columns of an NSTableView. I want to bind the titles of the columns of the NSTableView to the name property of the corresponding GHColumn.
My question: is there a way to do this through Cocoa Bindings, and if so: how? Or do I need to manually implement the data source for the NSTableView?
You will need to use an NSArrayController. Bind its Content Array binding to the mutable array on your GHTable object.
In the NSTableView, bind Content to the NSArrayController's arrangedObjects contoller key.
In the NSTableView's column, bind Value to the NSArrayController's arrangedObjects controller key with the model key path name.
If the inspector window shows "Scroll View Bindings" as its title when you click the table view, click it again on the content area and it should change to "Table View Bindings".
Click again on the table's column to select it and the title should change to "Table Column Bindings".

Bind to NSTreeController selectionIndexPaths

I want to bind to a NSTreeController's selectionIndexPaths programatically by doing the following (so that I can get a string a selection and display in a text view)
[activePDFView bind:#"name"
toObject:treeController
withKeyPath:#"selectionIndexPaths.nodeName"
options:options];
The tree controller is bound to a NSMutableArray that contains objects with the "nodeName" property. The object inside the NSMutableArray is KVC compliant for the property "nodeName" since I've implemented the proper accessors.
When I compile, I get the following message
'[<__NSArray0 0x1001698d0>
addObserver:forKeyPath:options:context:]
is not supported. Key path: nodeName'
I am not quite sure but is my binding correct?
Thanks.
It looks like what you want to bind to is not selectionIndexPaths, but instead the selection binding. The selectionIndexPaths binding will return an array of NSIndexPath objects, which is typically only used when binding an outline/browser view's selection to the tree controller. selection actually returns a proxy object which can represent either a single or multiple selection, and passes through any KVC requests to the underlying selected object(s). It's defined in NSObjectController, which is a superclass of NSTreeController. In your case, you would want:
[activePDFView bind:#"name" toObject:treeController withKeyPath:#"selection.nodeName" options:options];

Inserting image into NSTableView using bindings

I've an NSTableView bound to an NSArrayController with two columns. One column is bound to the arranged objects of the array controller and displays a string.
I'd like to display an image in the other column, but I just can't make it work. I've dragged an NSImageCell to the column and set the image by hand but it won't show up at runtime. I've double checked and the image is in my resources directory.
Am I missing something? What else should I do to make that image appear?
So you want to have the same image appear for each row? Is that why you're setting it "by hand"? For that, you can mix NSTableViewDataSource methods with bindings. The idea is your string column will be bound as usual, but your image column isn't bound. It has its identifier set (like "imageColumn"). You then use numberOfRowsInTableView: and tableView:objectValueForTableColumn:row: to provide the array controller's object count (so it has the right number of rows) and simply always return your static image when it asks for the value for the right column (checking the id for your "imageColumn"), returning nil otherwise.
If the image is not static (that is, you want to use it as an indicator of some kind) you can use the above method (return some image based some value) OR bindings. To use Bindings, you might add a property to whatever class your array controller is holding, like "status" (a number). You'll then use a custom NSValueTransformer that transforms the status number into a corresponding image. Your column will be bound to the array controller's arrangedObjects.status, using the value transformer (see NSValueTransformer for instructions for use - you have to register it, then use its name). The result is an image in your column that corresponds to a certain status.