Inserting image into NSTableView using bindings - objective-c

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.

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.

Creating an array item with a property that's dependent on an aggregate function on the array itself

Suppose I have an NSArrayController containing a number of 'blob' objects. These objects are displayed in a view-based NSTableView via bindings.
Now suppose that each 'blob' object contains a property called amount. For one of the NSViews in each row of the table I'd like to display amount / max_amount_in_array.
In other words, I somehow need to bind my cell to the NSArrayController's arrangedObjects.#max.amount and to the NSTableViewCell's objectValue.amount at the same time and perform my calculation.
Is there a way to handle this nicely using bindings?
Currently the only idea I have to is to have a ratio property in 'blob' and recalculate it myself every time that an object is added to the array. That's quite possible, but it just seems like there should be a more bindings-like way to solve the problem.
I've now done this like so:
[libraryCell.myView bind:#"amount"
toObject:libraryCell
withKeyPath:#"objectValue.amount"
options:nil];
[libraryCell.myView bind:#"max"
toObject:_librariesController
withKeyPath:#"arrangedObjects.#max.amount"
options:nil];
There are thus two properties in myView (amount and max) and when either of them change I do the calculation and update the display accordingly.

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.

The textfield displayed a value which is the calculate result from an action, how to bind the value to core data

I'm working on mac app project. I have a table and a textfield, one column named price, and the textfield names price as well. I binding the column price to a controller with keypath arrangedObject, for the textfield, I bind to the same controller, with keypath selection. It worked. It can read the data from user input to textfield.
However now, I need to calculate the number then display the number to the textfield, and then bind the value. so that the column can also display the value. How to do that?
If I understand you correctly, you are already successfully doing the bind part. So what you need to do is set a calculated value in the text field - which, due to the binding, will then also be shown in the appropriate cell in the table.
To set a value in a text field, you create a new outlet (yourTextFieldName) in the controller class by control-click dragging to a blank line in the controller class .h file - but I'm guessing you are far enough along to know that. Then to set the value use
[yourTextFieldName aString];
Presuming that your data is numeric (as you refer to computation) you may have to convert it to a string first, possibly with
[[NSString alloc] initWithFormat:#"%#", numericValue ]; // with NSNumber *numericValue or %g if it is a float, or...

Getting the displayed image out of an NSImageCell

I have an NSImageCell table column whose valuePath is bound to a path supplied by my object through an NSArrayController.
My NSTableViewDelegate implements the -tableView:heightOfRow: method, in order to have variable row height. I need to calculate row height based on the dimensions of the image displayed in the aforementioned column.
Right now, I'm getting horrible performance, though, since I'm calling [[NSImage alloc] initWithContentsOfFile:<path>] for each iteration. Is there any way to load the image representation that each NSImageCell has already retrieved for display?
I'm happy with the performance exhibited by using the valuePath binding, and would rather not cache each image on my own, as there will be many of them, each somewhat large.
I tried the NSTableColumn method -dataCellForRow:, which sounded perfect, except the cell returned returns an objectValue that seems to be the last row for which data was loaded.
Update (solution, unsatisfactory)
I figured out an approximate solution (posted simultaneously below), but it seems clumsy (and I've already seen it fail at random, irreproducible times), and I am still looking for a better solution.
I'm using the -tableView:willDisplayCell:forTableColumn:row: delegate method to populate a mutable dictionary with [[cell objectValue] size] (the image's size) keyed to the represented object's unique ID. Then, in the -tableView:heightOfRow: call I'm looking up the cover from this dictionary. I need to call [tableView noteNumberOfRowsChanged] after data is loaded, otherwise the dictionary isn't filled properly for the initial screen of data.
I tried the NSTableColumn method -dataCellForRow:, which sounded perfect, except the cell returned returns an objectValue that seems to be the last row for which data was loaded.
That's because the NSTableColumn only uses a single data cell for displaying the entire column, swapping out its value as it draws.
If I were you, I would probably try implementing the – tableView:willDisplayCell:forTableColumn:row: method in your NSTableViewDelegate. Then you can intercept the cell before it's about to be drawn, get its value at that moment, and cache just its height (as an NSNumber* or CGFloat). Then you can return that value in -tableView:heightOfRow:.
Then again, it's possible that -tableView:heightOfRow: gets called before – tableView:willDisplayCell:forTableColumn:row:, so that might not work. I'm not sure. But that's where I would start.