NSArrayControllers and heterogeneous arrays of Core Data objects - cocoa

I'm trying to create a Mac OS Core Data application that has an array of parent objects (called Levels) each of which contains a collection of child objects (called Blocks) via a one-to-many relationship. I have a table view successfully controlling the array of levels, and a custom view object that draws the blocks graphically based on positions held in x and y properties of my Block model class. I can add blocks to the currently selected level, remove them, select and move them around in the custom view, and have bound text fields to various other properties of the Block class which I can use to edit those values. All of this information is successfully saved and restored to and from the core data repository with no issues output to the debugger. Wonderful. I've used an NSArrayController for the Levels and another for the Blocks that is bound to the current selection of the Levels array controller, in what I've read is a pretty standard way.
Now, my Block class is actually an abstract class, and what I actually instantiate are various child classes of Block (eg RedBlock, GreenBlock, BlueBlock classes). Each sub-class has a separate set of properties that only apply to that type of block (so RedBlock has a "text" property that none of the others have, BlueBlock has an integer "value" property, etc). I want to create an inspector that will change depending on the type of the Blocks that are currently selected in my custom view. To try this, before I start creating subviews for each type of Bock, I have created a text field that I want to bind to the currently selected RedBlock's "text" property, preferably showing nothing when Blocks of other kinds are selected. This is where I'm stuck. I've added another NSArrayController in Entity mode with RedBlock specified as it's type so I can bind to the "text" property, and tried adding a filter predicate based on the class type. I've also tried various other configurations and bindings, but I'm either getting exceptions, or corrupt values in the text field that I bind to that controller, or other weird bugs and general brokenness.
I've googled around for an example of an inspector that can cope with a heterogenous array of objects (as that's essentially what I'm trying to do) but so far no luck.
So, my question is - am I going about this the right way? Should I be trying to create an NSArrayController that filters the selected items in my Blocks array controller somehow? If so, should that be straightforward or is there some trick that I've missed? If not, what is the best way to do this?

This approach should work, provided you limit the inspector to displaying view which bind to properties which apply to the entire selection. You don't need a second array controller.
To test the simple example, try creating a data set with only blocks, see that your bound control loads without raising exceptions, and that it updates the object correctly.
Once that is working, create separate views for each type, and display and hide them when the selection will change. Again, if you have a heterogenous selection, hide them all.

Related

Bind each row of NSTableView to separate property of object?

I have a main NSArrayController bound to an NSTableView with each row containing an instance of MyObject. MyObject has about 30 properties, but the NSTableView only has 5 columns (to show the most important properties). When a row (or more) is selected I have another NSTableView (a detail view) which shows all the properties, one per row. The detail table has two columns, one for the property name and one for the property value.
I have this working right now but my detail NSTableView uses a data source rather than bindings. This works fine as long as I notify my detail controller (which manages the data source) of a change in the main selection so that it can reload the detail table.
While it is easy to detect a change in selection, it is harder to detect a change in one of the properties of a selected object. I see elsewhere on StackOverflow, that one way to do this is to have a dummy property and use keyPathsForValuesAffectingValueForKey. Does this work well performance-wise?
Is there some other/better way to build my detail NSTableView/NSArrayController so that each row represents one property of the selected objects on the main NSTableView/NSArrayController? I'd like to use bindings if possible.

Cocoa bindings issue with three tableviews

im a osx-dev noob that is trying to build an application with three table views that will show the content of a core data store entity. But each table view is filtered on the attribute "status" of the entity.
the problem occurs when i also want to show the selected entity in textfields. I'm using three different array controllers with different fetch predicates. But in a textfield i can only bind the value to one array controller.
should i ditch the bindings and do it all programaticly or is there a simple solution to this? :)
here is an screenshot so you can grasp my app description.
Keep bindings to populate the text fields if it satisfies what you want to do with this GUI. I'd add an NSObjectController to control the one entity those fields represent. If you want the user's changes to those fields persisted, bindings are still awesome.
But I think with three tables that might control what's displayed in the text fields, you're going to need to have some sort of non-binding glue code that determines which of the tables wins. You can probably do everything you want by implementing some of the NSTableViewDelegate protocol.
If the text fields should display the last entity that the user clicked in any of the tables, simply have each table call the same tableViewSelectionDidChange delegate function. All three tables could have the same delegate. You can then call setContent on the NSObjectController from that function.
You could also use similar glue code to prevent more than one selection in any of the three tables, by having the same delegate function deselect everything in the other tables either through the view or the controller. But that's up to you and needs consideration of whether you want multiple selection, etc.

Using NSArrayController to bind model objects to an inspector view, how to catch special cases like empty and multiple selections?

I have inspector view and because it contains a lot of UI I want to explore cocoa bindings to connect it to the model. The inspector view will update depending on the currently selected object in an outline view and the inspector view displays controls to alter properties (like colour, name, shape etc.) that can be changed.
This works well when only one row in the outline view is selected because the view has a one-to-one relationship with the model object. However, there are two special cases that I want to be able to catch:
Empty selection
If no items in the outline view are selected I want to display a special "No items selected view" placeholder view.
Multiple selection
If multiple items are selected I can either display a "Multiple items selected view" placeholder view or I can decide to let the changes apply globally to all selected model objects.
My question is how can these special cases be caught when using bindings? Moreover, how can I do behaviour "A" when there is an empty selection and behaviour "B" when there is a multiple section. This sort of thing is trivial when using target/action because you can just code the logic into the action method.
Bindings seem great when propagating changes to values but I'm unsure how to go about implementing bindings that require different decisions based on current selection state.

Is it possible to bind an NSTreeController to an NSOutlineViewDataSource?

I have some hierarchical data model that I'd like to present in an NSOutlineView. I'm binding a tree controller to the outline view to provide data and to handle selection and binding to other views.
However, I only want to show only part of the data in my model to the outline view. (Each object in my hierarchy has an array of child objects, but I'd only like some of these child objects to appear as child nodes of the node in the tree.) I wish I could just attach a filter predicate to the tree controller, but it seems that NSOutlineView doesn't support filter predicates.
I think that this design requires an NSOutlineViewDataSource to filter my data model, and an NSTreeController bound to the data source and the outline view. However, none of the binding outlets in the tree controller (Content Array, Content Object, Content Set, etc.) seems appropriate for binding a data source.
Any ideas? Thanks in advance...
You can try feeding the data to your array of child objects through an array controller.
Here's how I'd do it. Override the accessor method in your represented object and return a filtered array from your array controller.
In general, NS[Outline|Table]ViewDataSource and Cocoa Bindings is an "either/or" choice. Mixing the approaches, while perhaps not strictly impossible, will likely lead to unpredictable results.
You mention binding a filter predicate to the Outline View itself and not to specific nodes, so I surmise that one filter predicate for all nodes might be "good enough." If that's the case, then one solution to this would be to expose a second children-vending property on your model, maybe filteredChildren, and tell the NSOutlineView to use that to access children instead of the your default/complete children-vending property. If you need functionality like drag reordering, this approach might prove to be non-trivial, but it should be easy to explore this approach regardless.
If you need a different filter for each node, or if the filter changes dynamically, this task would likely have crossed over into being a case that was a good candidate for implementing NSOutlineViewDataSource (and a poor candidate for using Cocoa Bindings.)

Where are NSArrayController's bindings declared?

When binding a NSArrayController, I see the following bindings available under controller content tab
Content Array
Content Array for Multiple Selection
Content Object
Content Set
However, I couldn't find those options inside the Apple reference for NSArrayController, nor inside the actual NSArrayController.h itself. Where do they come from? And how do they differ from the arrangedObject property?
Screenshot Below
They're documented in the Cocoa Bindings Reference, and they're not publicly declared in any header file. Xcode's knowledge of them is baked into Xcode, probably in some plug-in somewhere that comes with it.
And how do they differ from the arrangedObject property?
arrangedObjects (plural) is an output. The array controller arranges its content objects, and the result of that is its arranged objects.
The bindings are inputs:
contentArray is the main one, since it is an array controller.
contentSet is an alternative for binding to Core Data to-many relationships, which are sets. I'm not sure whether it works with ordered sets; when the set isn't ordered, the order used by arrangeObjects: and manifest in arrangedObjects is determined by the array controller's sort descriptors.
contentArrayForMultipleSelection, you don't normally need. The case in which you do need it is described quite well in the Cocoa Bindings Reference.
contentObject is, as the CBR says, another array controller. Again, see the docs for its description of when you'd need it.
Content comes from one (or more) of the content bindings, is arranged (according to any sort descriptors the array controller may have) by arrangeObjects:, and is then available in the appointed order under arrangedObjects.

Resources