NSTable CoreData and selection - cocoa

I have a very simple App. It is a non-document based CoreData app. The nib contains one NSArrayController and a window with a NSTable with one column. Below the table are three buttons, Add, Delete, and Fetch. Below the buttons I have one NSTextField and a fourth button labeled Save.
My model has two Entities, Department an Employee. The Department has one attribute, name. I'm not currently using Employee.
In the AppDelegate.h I have added this code:
#interface HMGAppDelegate : NSObject <NSApplicationDelegate>
{
NSArrayController *_departments;
}
#property (retain) IBOutlet NSArrayController *departments;
and in the AppDelegate.m
- (void) awakeFromNib
{
[[self departments] fetch:nil];
}
The buttons are bound to add: remove: and fetch: on the NSArrayController.
The save button is bound to save: on the AppDelegate
The NSTextField is bound to the instance of the NSArrayController in the nib, ControllerKey is selection, Model Key Path is: name
When I run the app, I can create new entities and change the value of the entities. That all works fine.
What doesn't work is selection in the table. Changing the selected row in the table does not cause the value from that row to be selected in the NSArrayController. I understand I could put a delegate on the table catch SelectionDidChange and explicitly set the selection on the ArrayController, but I wonder if there is an easier way to do this.
I have seen other examples where there are two table views, representing a one to many relationship. Selection in the first table causes the second table to display the dependent entities. (for example choosing a department shows the employees of that department). In the example I'm looking at there doesn't seem to be a NSTableDelegate.
What am I missing? How does the NSTableView communicate its selection back to the NSArrayController?

How did you setup the Binding? If you bind NSTableView's content to NSArrayController, you have to bind the selection & sortDescriptions as well.
As an alternative, you can bind the table column's Value Binding to Array Controller.arrangedObjects and the selection should work.

Related

Cocoa binding NSTableView within NSTableView

I'm trying to display a NSTableView inside a NSTableView. This is for an iTunes-like albums/tracks view. So there's a list of 10 albums, each album has some tracks. I want to display the albums in the outer table view and the tracks for each album in the inner.
The first NSTableView is bound to an NSArrayController. Each object in the array has its own "tracks" NSArrayController, but I can't figure out how to tell the 'tracks' NSTableView that its content comes from a property of the 'album' NSTableView.
If I understand you right, the source content of the nested array controller comes from objectValue of the owner table cell. So you can't put the array controller content source to be the objectValue of the table cell. I'm doing similar in that I want to filter the array content based on the object value
What I'm doing, which seems to be working, is to make a seperate nib file for your nested table cell view, with it's own nstablecellview subclass. Include the array controller in the nib and create an outlet to it in your cell view subclass.
Register it with the table view in the viewDidLoad method of the tables view controller:
NSNib *cellView = [[NSNib alloc] initWithNibNamed:#"MyTableCellView" bundle:nil];
[myTableView registerNib:cellView forIdentifier:#"myTableCellView"];
Then, in the awakeFromNib method of your cell view subclass, manually make your bindings that require the object value:
[self.arrayController bind:#"contentSet"
toObject:self
withKeyPath:#"objectValue.tracks"
options:nil];
Voila.
Note that when using this technique, the files Owner of the nib file is not your nstablecellview subclass, it is the view controller of the table view.
The problem lies in not understanding the MVC-pattern (Model-View-Controller). Content for a view never comes from another view, it comes from model objects via a controller. The content of each of your tableviews always comes from an NSObjectController, or a subclass like NSArrayController. Basically, there are two solution:
bind the 'tracks' tableview to the selection of the 'album' array controller
create a 'tracks' array controller and bind it to the selection of the 'album' array controller. Bind the 'tracks' tableview to the 'tracks' array controller
A third solution is to use an NSTreeController with an NSOutlineView but outline views and tree controllers are notoriously hard to work with.

NSPopupButton not updating when using setValue:forKey: against it's CoreData bound field

I'm working on a CoreData / Document based app. In one area of the UI I've setup a view-mode table with various columns. One column has an NSPopupButton in it with the Selected Index binding setup as Table Cell View.objectValue.startupState.
Picking any of the menu items in the popup will correctly update the startupState attribute on the entity with the index of the menu item clicked and the NSPopupButton text updates as well. I've verified the attribute value is in fact updated by saving, closing, and re-opening the document.
In another column I have an NSPopupButton bound similarly to another attribute in the same entity - Table Cell View.objectValue.mode. Depending on the mode selection it will modify the startupState value through a manual implementation of setMode which does this statement in certain cases:
[self setValue:[[NSNumber alloc] initWithInt:1] forKey:#"startupState"];
The issue I'm having is that the NSPopupButton isn't updating to show the menu item text for the selected index. As before, I saved, closed, and re-opened the document after the above code ran and the correct item was selected / text appeared so I know the setValue call updated the attribute.
Any ideas?
As mentioned in the comments, Volker's suggestion addresses the issue. willChangeValueForKey and didChangeValueForKey messages are needed around the setValue:forKey call like this:
[self willChangeValueForKey:#"startupState"];
[self setValue:[[NSNumber alloc] initWithInt:1] forKey:#"startupState"];
[self didChangeValueForKey:#"startupState"];

NSTableView rebuilds all cells when adding only one

I have a view-based NSTableView driven by an NSArrayController bound to a NSMutableArray as the content array.
When I add a new item to the data source, tableView:viewForTableColumn:row: is called for every item in the data source. I would expect the table view to leave existing items alone, and only call this for the new item.
I add a new item via the controller, like this:
[_arrayController addObject:newObject];
How do I prevent the table view from doing this? The performance impact is unacceptable.
Edit: It also clears the user selection

In a tableview with a NSButtonCell in a column, who creates NSButtonCell(s)?

A TableView is created in IB, and it is desired that it have only one column with checkboxes. One way to create the checkboxes is to drag an NSButtonCell into the column using IB, and then conforming to the NSTableViewDataSource protocol implement:
- (void)tableView:(NSTableView *)aTableView setObjectValue:(id)anObject forTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView
Hence when a class object is acting as a datasource to a column in a TableView, and the column contains objects such as checkboxes for each row, is the class supplying the only the on/off state data for the checkboxes, or is it also supplying the checkbox objects?
Related questions are:
Is the TableView creating and containing the NSButtonCells?
If the answer to 1 is yes, how can one set/change the NSButtonCell's properties, like its title?
Rather than the TableView creating and maintaining the NSButtonCells, can the datasource class create them? That is, instead of using IB to drag the NSButtonCell to the column, can one add the NSButtonCells in a method such as awakeFromNIB?
Is there a way to mix the types of cell objects in a given column. For example, could one have a text heading in column1row1 followed by a checkbox in column1row2?
Thanks.
I think you are finding it difficult to understand the difference between Model,View and the Controller.
Your Class object should only act as "Model" - in your case supply the on/off state data.
Your IB provides the "View" part ,Normally you should use it for creating any user interface.
Your View Controller class which impliments tableview delegate/datsource methods is the"Controller" part and you should use it as a mediator between View and Model.
You can choose to provide button state along with title in your Model.
and can set it in your Controllers implementation of the delegate/datasource method.
Yes, you can create button cells in your controller but avoid it unless it is absolutely necessary.
You can mix different types of cells,Instead you can choose to look into the View based TableView also.

NSArrayController "Content set" bound to NSTreeController issue

I have an NSOutlineView bound to a NSTreeController and a CoreData Datamodel.
NSOutlineView displays his data properly.
A NSTableView's cell values bound to a NSArrayController, displaying data from CoreData.
NSTableview displays his data properly too.
The Datamodel has a relationship between data for NSOutlineView and NSTableView
When I try to bind NSArrayController's "Content set" to NSTreeController.selection.name
to display all items related to the NSOutlineView selection I get this error:
Cannot create NSSet from object Untitled of class NSCFString
(Where "Untitled" is the value of NSOutlineViews node)
And no data in NSTableView is displayed.
Everything setup in IB - does anyone has a hint for me to get this working?
Thanks a lot!
You want to bind the NSArrayController's Content Set to NSTreeController, it's Controller Key to "selection" and then the Model Key Path should be the relationship name, which I would hope isn't "name". Then in the TableView you bind the column(s)'s value to the NSArrayController, with Controller Key being "arrangedObjects" and Model Key Path the property "name"

Resources