Binding columns in an NSTableView - objective-c

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".

Related

How to bind different entities with NSTableView and NSTabView?

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.

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

Auto-sort table column contents bound to NSArrayController

So, this my case :
I've got an NSMutableArray (of NSMutableDictionary instances) bound to an NSArrayController. Each element's name value is, in turn, bound to the value of the first table column.
The thing is :
How should I make it auto-sort the elements in that specific table column (of my NSTableView) ?
Is the column sortable through user action? For the table column's Value binding, you can enable the Creates Sort Descriptor option. That's enabled by default, usually. You should also set the Sort Key and Selector in the attributes inspector.
If you're trying to make the table sorted when the window is first loaded, you should construct an array of NSSortDescriptors and pass that to the array controller's -setSortDescriptors: method. A good time to do this is in an override of -windowDidLoad in the window controller or during -awakeFromNib.

Sorting NSTableColumn with NSArrayController

I have a NSArrayController bound to an NSTableView so the table column like the following:
NSTableView bindings:
Content -> ArrayController.arrangedObjects
SelectionIndexs -> ArrayController.arrangedObjects
SortDescriptors -> ArrayController.sortDescriptors
NSTableColumn bindings:
Value -> ArrayController.arrangedObjects.description
When I try and sort it using the column header it just crashes with something like
error setting value for key path sortDescriptors of object NSArrayController
Any ideas?
I struggled with the exact same problem today.
It seems that binding the content and selectionIndexes of the tableView to the array controller IB > inspector window > select your tableView > bindings tab, disables the sorting by clicking on the table header. This makes sense, because the table view now shows you the exact contents (and ordering) of the array controller.
I unchecked these bindings in the IB, also removed any sort keys from the table columns IB > inspector window > select your NSTableColumn > attributes pane. Select the checkbox Creates Sort Descriptor in the table column's binding tab. No sortDescriptor is needed on the table, although I think binding the table's sortDescriptor to Shared User Defaults Controller saves the ordering when you quit your application.
If you need to sort your table, put a sortDescriptor on the array controller, maybe in the awakeFromNib.
- (void)awakeFromNib {
[super awakeFromNib];
[self setSortDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"propertyOfYourObject" ascending:YES selector:#selector(compare:)]]];
}
This does not interfere with clicking the table column headers.
I couldn't get a sortDescriptor on the array controller to work with bindings.
I see several issues in your binding attempt.
One doesn't usually need to bind the NSTableView at all. Binding the values of specific NSTableColumns to the NSArrayController is enough.
You try to bind something against a .description property. Please remember - "description" is like a "reserved word" in Obj-C. Any NSObject should present itself as NSString in its "description" method. This is what is called when you po <object> in the debugger, or pass an NSObject to NSLog via "%#". So... probably you'd want to rename your property to something else.
You do NOT need to bind the sort descriptors of the NSArrayController or the NSTableView or the NSTableColumn at all. As it happens, when you bind an NSTableColumn's value to the NSArrayController's arrangedObjects, the NSTableColumn (actually the NSColumnHeader) object knows to set the NSArrayController's sortDescriptor to the same path as the one you specified for the column's value binding - as you click on the column header. In other words - sorting by clicking on column headers comes free, if you just bind your column's value to the NSArrayController's arrangedObjects.
Documentation on table binding is bad and frustrating. There are several different schemes for working with a Table, and debugging binding problems is a real nightmare. However, there are plenty essays and tutorials on the net for this.
Hope this helps.
Let me suggest you a simple way to do this -
NSTableColumn bindings:
Value ->
Bind to: ArrayController
Controller Key : arrangedObjects
Model Key Path : keyPath (such as name)
If you are new to using bindings with table view, this article will be of great help to you-
EDIT: Project relocated to Github. (No more explanation - code only)
NSTableView, NSArrayController and More Bindings

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.