Cocoa binding NSTableView within NSTableView - macos

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.

Related

How is NSTableView Content Sorted After Array Controller Add?

After adding a managed object in array controller class, the bound table view 'places' it into the correct date sorted order.
However, as the table view is building it's rows, the new object has been placed at the bottom of the array controller's content array using:
Edit: Solution: Don't use the array controller's content. The issue was in:
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
....
// originally
NSManagedObject *ci = [[self.arrayController content] objectAtIndex:row];
// should be: (note arrangedObjects replacing content)
NSManagedObject *ci = [self.arrayController.arrangedObjects objectAtIndex:row];
....
}
The image below shows an example of the placement. Console output from within -tableView: viewForTableColumn: row: at left (from original code above) and ordering in the table view on right. LHR is the new object (notice date order ascending).
Tried:
Saving the moc right after adding newObject
Calling Array Controller's arrangeObjects method
Attempting to manually setSortDescriptors
Binding the table view's selection index to the array controller (for grins)
This is also messing up the selectedRow even though the row with LHR is selected in the table view right after adding. The only way to correct the array controller's order is to manually sort the column.
How can the array controller's content be in sync with the table view? Even more, why might they not be in the same order?
The array controller "arranges" its content. It does this by calling its -arrangeObjects: method. The default implementation filters the contents using the array controller's filterPredicate and then sorts it using its sortDescriptors. A subclass could override that method to arrange the content differently.
The arranged contents can be accessed using the arrangedObjects property. This is what corresponds to rows in the table view. So, you should always use this if you're indexing by a table row. For example:
NSManagedObject *ci = [self.arrayController.arrangedObjects objectAtIndex:row];
The array controller's sortDescriptors may be set via bindings (e.g. if the table view's sortDescriptors binding is bound to the array controller). Depending on how you set up the table view bindings, this may be automatic. For NSCell-based table views, you typically bind the table columns value binding and don't bind the table views bindings. In that case, the table view automatically binds its content, selectionIndexes, and sortDescriptors bindings to the same controller as its columns. For view-based table views, you typically don't bind table columns and have to bind those table view bindings explicitly if you want them bound.
You can also set the array controller's sortDescriptors programmatically.
Try invoking rearrangeObjects on the NSArrayController and then doing the selection you need subsequent to that in a dispatch_async block. It's maddening, but some of the bindings are deferred to the next runloop turn, so you can't simply add an object, call rearrangeObjects, and select the new object in synchronous code. You have to use GCD to schedule things
See this question/answer for more discussion. It's is the exact same issue. Adding an object and trying to select it.
Enabling the Auto Rearrange Content option of the Array Controller made it for me.

Receive new value from edited cell in View-Based Table View

I have an View-Based Table View with some standard NSTableCellView data rows, which I fill from my data source. User can edit text field of a cell. Please, explain me, how to receive the new value, in order to I can update my data source, when user change text in cell?
I think the best way is to have a controller object with a xib file that contains your table view (the controller could be a subclass of NSViewController). Make this controller the delegate of your table view and put an IBAction connected to your text field in this class. You should also have an outlet to the table view. Then, in your action method for the text filed, you can get the row that contains the text field from [tableView rowForView:sender]. Once you have the row, you can update the proper object in your array.

NSTable CoreData and selection

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.

Populating an NSPopUpButtonCell with string values

I am trying to populate a NSPopUpButtonCell with a list of strings. In -(init), I populate an NSArray with the values I want in the PopUp Button. How do I connect this to the NSArrayController I added in IB? Does my app delegate need an IBOutlet NSArrayController to connect to or is there a way to bind it?
Also, when I bind the NSArrayController to the NSPopUpButtonCell, do which Content do I bind it to? Content or Content Values?
jorj
Bind the array controller's Content Array to your controller's array of strings. Bind both the pop-up button cell's Content and Content Values to your array controller's arrangedObjects.
Presumably, you also want to know which of these strings is selected. To do that, bind the pop-up button cell's Selected Object (which will be one of the objects in Content) to a property of your controller (the one that owns the original array).

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