Where are NSArrayController's bindings declared? - cocoa

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.

Related

Syntax of Model Key Path - Bindings xcode 5.1.1

Hey I am new to mac development and I want to use bindings (xcode 5.1.1).
I want to set the Title of a radio button dynamically by an entry of an array controller. I am looking for something like a syntax description how I can perform it.
e.g. something like value1 WHERE value2="bla"
If I trying to search at google I always find solutions which did it programmatically.
Is there anywhere some examples which show me the syntax I can use in this field?
The picture below should you show what I mean.
Answering the question as clarified in the comments…
First, bindings is not always the right technology. It can simplify some things, but it can't do everything and even for some of the things that it can do, it doesn't necessarily make them simpler.
Radio buttons are often organized in an NSMatrix. In that case, you can bind the matrix bindings to track the selection. There are three content-related bindings for a matrix, which can be kind of confusing. The "content" binding is the base. In some cases, it's sufficient. However, if there's a distinction between the object being bound and the value that should be shown by the cells of the matrix, then you can bind the "contentValues" binding to be a subpath of the content binding. That is, it needs to be the same as the content binding with possibly additional elements added to the end of the model key path.
Furthermore, if you want the selected object to be distinct from the content object, you can bind "contentObjects" to a subpath of the content binding.
For example, there may be an array controller whose content is a bunch of Person objects. The matrix content binding might be bound to that array controller's arrangedObjects. If you leave it like that, the cells of the matrix will be populated from the description of each Person object. However, you could bind the matrix's contentValues to the array controller, arrangedObjects, model key path fullName. Then, the matrix cells will be populated with the full name of each Person object.
If you then bind the matrix's selectedObject binding to a property on your window controller, that property will be set to the selected Person object each time the matrix selection changes. If you would prefer, you could bind the matrix's contentObjects binding to the array controller, arrangedObjects, model key path uniqueID. In that case, the window controller property would not be set to the selected Person object itself, but to its uniqueID property.
Alternatively, you could bind the matrix's selectedIndex binding to a controller property. If you use the window controller, then that just directly sets a property on the window controller to indicate the index of the matrix's selection. Or you could bind it to the array controller's selectedIndex property, in which case the selection is "stored" in the array controller.
You need a keypath that takes no parameter as described in the key-value coding (KVC) reference.
By binding to an array controller's selection, if the selection collection is one object with a property or method "value1," then the binding runtime is calling the method valueForKeyPath:#"value1".
The NSObject protocol has performSelector:withObject, but there is nothing like valueForKeyPath:withObject in the KVC protocol or the NSKeyValueBindingCreation protocol
That said, registering dependent keypaths can provide some equivalent behavior...
+ (NSSet*) keyPathsForValuesAffectingValue1
{
return [NSSet setWithObjects:#"value2",nil];
}
... and that would ensure that any time value2 changes, the binding to value1 is re-evaluated.

How to sort NSArrayController using bindings from NSTableColumn?

There are similar questions to this already, but I haven't found any of them to work for me. All I want to do is re-sort the table view based on clicking the column header. And I don't want to do this programmatically because I don't think I have to. Anyways...
I have a NSTableView binded to an NSArrayController that is binded to my managedObjectContext.
For a specific NSTableColumn, I have the value binded to the Array Controller with the model key path "amount" which is an NSNumber.
I see in the attributes for this NSTableColumn, there is a "Sort Key" and a "Selector". I set the sort key to "value.amount" (not sure why, but I saw this elsewhere) and the selector defaults to "compare:" which seems fine.
Then for the NSArrayController, I see a binding called "Sort Descriptors" which I bind to "Shared User Defaults Controller". The "Controller Key" defaults to "value" (I assume this is why I used value before), and I set the "Model Key Path" to "amount.
This sounds reasonable, I suppose. But (1) it doesn't work and (2) what about when I want to sort based on two different columns? Also, (3) how do I set the default way to sort?
UPDATE:
OK, so I have the array controller binded to the managed object context. I have the table content binded to the array controller arranged objects, and selection indexes to the array controller selectionIndexes. I have a table column value binded to array controller arrange objects and the associated key. the sort key for that column is the same key with a compare: selector.
The table is not reordering when I click the headers though. Am I supposed to bind the sort descriptors of the table?
If possible, can you send me your example?
Thanks for the help,
Chet
First thing you should know is both the NSTableView and the NSArrayController can sort your items. You’re trying to set a mix of both right now, and there’s no point in that. For simple uses, just rely on the tableView.
So, don’t bind anything in the NSArrayController. For the NSTableColumns, don’t use “value.amount”, just use “amount”. “compare:” is fine for the selector.
I just created a tiny project (no code) to demonstrate this and it worked great with sorting.

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

How to bind objects in NSDictionary in NSUserDefaults?

How do I bind NSDictionary objects to several text fields in Interface Builder?
I would like to bind each object to a specific item in the dictionary. For example, the first text field should be bound to Actor:
You need to add a NSObjectController in Interface Builder. In the attributes inspector tab leave the standard mode "Class" and class name on "NSMutableDictionary", but switch "Prepares Content" on. In the bindings inspector bind "Content Object" to "Shared User Defaults Controller", controller key "values" and "Model Key Path" to your dictionary's key in User Defaults. Switch "Handles Content As Compound Value" on.
Now you can bind your text field (or check boxes etc). values to the ObjectController, with controller key "selection" (!) and the keys in your dictionary (as model key path).
Hope this helps. I couldn't find the answer in Apple's documentation and also not in the net, was lucky to find it out myself by trying different controller objects...
First: #keeluu's answer should be marked correct.
Doing it Programmatically
If you're doing this in code rather than through IB, there's a "gotcha" to look out for. It looks like this:
// Assume 'anObjectController' is an NSObjectController.
// Assume 'userDefaultsController' is [NSUserDefaultsController sharedUserDefaultsController]
// Assume 'someDictionary' is an NSDictionary in userDefaults.
[self.anObjectController
bind:NSContentBinding
toObject:userDefaultsController
withKeyPath:#"values.someDictionary"
options:#{NSHandlesContentAsCompoundValueBindingOption: #YES}];
If you do the above, you'll find that when you bind your UI elements to properties in someDictionary, those UI elements will correctly display the values that are IN user defaults, but when you change them (by say, clicking a bound checkbox) those values will NOT be updated in user defaults; the old values stick around.
The Reason
The binding must be to NSContentObjectBinding instead of NSContentBinding. The NSObjectController class exposes a binding named contentObject. When you bind to that, everything works properly.
This is an easy mistake to make because Xcode's code completion automatically goes for NSContentBinding and it's one of the most frequently used binding names anyway. This just cost me hours of trouble, so hopefully I saved someone else some time.
Try to add NSDictionaryController to you .xib file, then bind its Controller Content to your dictionary in User Defaults. Then you can bind text field values to Dictionary Controller (arrangedObjects key).
I never tried that but I think it must work.
Hope, it helps.

NSArrayControllers and heterogeneous arrays of Core Data objects

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.

Resources