I can't find in the Docs where they explain all those fields and what they mean. Especially "Controller Key" is not clear to me.
[Copying my answer on another question…]
The controller key is the key for the (property of the controller object) you're binding to. The model key path is the key path by which the bound object can ask the model objects for more basic objects, such as strings or images, or for other model objects (i.e., drill down into the model).
An example: Let's say you have a Person objects in an array controller, and each Person has a name. You bind a table column to the array controller, controller key arrangedObjects (thereby getting the model objects), model key path name (thereby getting the value objects).
A more complex example: Suppose you have an array controller of Departments. Each Department contains Persons (employees in the department). You can bind your People array controller to the Departments controller, controller key arrangedObjects (getting the Department model objects), model key path #distinctUnionOfObjects.employees (getting the Person model objects), and then bind a table column to the People controller, controller key arrangedObjects, model key path name.
That table would be for people who work for your company; if you have a separate table of prospective employees, you can create Person objects for them, too, and they won't show up in the table of existing employees because they're not in a Department. When you hire them, you'll add them to one or more Departments; then, they'll show up in the People array controller automatically, because that array controller is observing the employees of all of the Departments.
The Controller Key pop-up menu is a way to help you discover what keys the controller (typically a NSArrayController, NSObjectController or a NSTreeController) presents.
The best example is the selection key of NSArrayControllers, which contains the set of selected objects. What is confusing is the NSObjectController presents a 'selection' key too, although the controller can control only a single object (therefore the selection = the object).
I agree that it is not clear at all. I personnally began to understand it when I bound my objects programmatically (i.e. using the bind:toObject:withKeyPath:options: method).
It has to do with key-value coding. You can bind a control in IB to a value in your controller. To connect that value, you have to specify the keypath to it. For example, if you have a textfield in IB and you want to bind it to say a field called 'name' in your controller, you would specify 'name' as the keypath. You then need to set up your name field in your controller to be accessible through key-value coding. This is done in 10.5 by using the #property and #synthesize specifiers.
Take a look at this topic: Cocoa Key Value Bindings: What are the explanations of the various options for Controller Key?
I posted an explanation of where to find definitions for all Controller Key's there.
Related
I have two entities in my Core Data project: Entity1 and Entity2. I've setup an array controller for Entity1 which displays a table view and a few properties in text fields next to it. It's a simple app for data entry, nothing fancy - all built using Cocoa Bindings. All values are saved and retrieved correctly for attributes on Entity1. Here's how I'm binding the text fields:
Value - Bind to Entity1ArrayController
Controller Key: selection
Model Key Path: attribute
Both entities have a one-to-one relationship and I would like to be able to bind an attribute from Entity2 to another text field so I can set a value just like it works so well above on Entity1. I assumed I would just bind this to the same array controller and enter the relationship and attribute as key path, like so:
Controller Key: selection
Model Key Path: relationship.attribute
Even though the code compiles, and I can enter a value, it isn't saved or retrieved. Where am I going wrong?
How does one bind an NSArrayController's content to the entities in an ordered to-many relationship?
I have an unordered to-many relationship in my Core Data model, and an NSArrayController whose Content Set is bound to that relationship from the parent entity. This works fine, the data is accessible from the NSArrayController, no problem.
I decided during development that it would be better to allow users to manually reorder these child objects, so I changed the relationship to an ordered one. Now, when my NSArrayController is being created from my nib, the following error is presented:
Cannot create NSSet from object Relationship '...' fault on managed object ... of class _NSFaultingMutableOrderedSet
Now actually, I think this all makes sense: It's an ordered relationship, so now I'm getting an ordered set. Binding it to Content Array also would be inappropriate, since it's now an NSOrderedSet, not an array. My question is: Now how do I bind this relationship's data back into the NSArrayController?
I came across this discussion while searching to see if there've been any new developments on this front. In a shipping app I currently bind the array controller's content array to orderedSetKey.#array and it works just fine, not sure if I discovered that myself or if someone else suggested it somewhere.
The fundamental problem is that a Core Data ordered to-many relationship returns an NSOrderedSet, and NSOrderedSet is not a subclass of NSSet. Any array controller bindings that expect an NSSet will fail.
Tom Fewster has a detailed blog post describing the use of NSValueTransformer to work around this shortcoming, converting between NSOrderedSet and NSArray on the fly. He also provides a sample implementation on Github.
I have a pretty straightforward MVC design question.
I've got a class with a bunch of properties, and a form to present an instance of the class. Several of the class properties are arrays - some are arrays of NSStrings to be presented in a one-dimensional table view, and some are arrays of sub-objects to be presented in a two-dimensional table view (one column per sub-object property). I don't actually want to do anything with the data in any of these tables - just present the contents in a scrollable, read-only table view.
During my first attempt at bindings, I added an object controller bound to the class instance. Then, I tried to bind each column of each table view to the "selection" member of the class, with a model key path specifying the array property of the instance (and, for the two-dimensional tables, a member of the sub-object). I was surprised that this didn't work for the columns of the one-dimensional tables.
Next, I added one array controller for every table, binding it to the "selection" member of the object controller. For the one-dimensional tables, I bound the column to the array controller with no model key path; for the two-dimensional tables, I bound the column to the array controller with a model key path specifying a property of the sub-object. This works - but for a window with seven tables, I have seven array controllers! That feels like overkill, since the tables aren't doing anything other than presenting data.
My question is simple: Is my design in line with good MVC practice - do I really need all of these array controllers? Or is there a simpler way to specify my bindings (for one-dimensional and/or two-dimensional tables) that will enable me to eliminate some array controllers? When I have an array of strings in an object to be displayed in a one-column table, it feels like overkill to use an array controller bound to the object and the table.
As an ancillary question - do I really need to worry about excessive array controllers? Are they lightweight objects that I should use liberally, or resource-intensive objects that I should conserve, especially in limited resource contexts like iOS?
Mac OS X. CoreData app. NSTableView controlled by NSArrayController bound to managed object context for the Country entity. The Country entity has a 'name' attribute and a to-many relationship, 'branches', to a Branch entity. The Branch entity has a 'sales' attribute (an NSNumber).
The NSTableView has two NSTableColumns. The first shows the name of the Country. The second should show the total sales for that Country across all its Branches.
The first column's value is bound to the NSArrayController's arrangedObjects with a model key path of 'name'. No problem there.
The second column's value is bound to the NSArrayController's arrangedObjects with a model key path of 'branches.#sum.sales'. This doesn't work. I get the error message: " addObserver:forKeyPath:options:context:] is not supported. Key path: #sum.sales"
If, instead, I add a 'totalSales' method to my Country class and the method is implemented as follows:
- (NSNumber *)totalSales
{
return [[self branches] valueForKeyPath:#"#sum.sales"];
}
and I then bind the column to 'totalSales' it works fine. My understanding of the Collection Operators documentation is that this should be the same as simply binding to 'branches.#sum.sales'. I can't see why the latter doesn't work. Any ideas? I have seen similar questions in this and other forums, but have yet to see an explanation or solution.
I don't know if this is still topic for you, but it surely does need an answer.
The second column's value should be bound to NSArrayController in exactly the sam way as first. I don't know why you made it differently and what actually you wanted to achieve.
Your first task was to bind table columns to array columns and this works the same way for all the columns and types.
Second task is to get sum of certain NSTableColumn bound to certain other object, like NSTextfield. And this is done like this:
[totalCountField bind: #"value" toObject: arrayController
withKeyPath:#"arrangedObjects.#sum.price" options:nil];
Let's take two Core Data entities, set up as follows:
Entity A: Car
Attributes:
year
model
Relationships:
manufacturer (<<-> Manufacturer)
Entity B: Manufacturer
Attributes:
name
country
Relationships:
cars (<->> Car)
Now, what I want to do is bind the display to an NSTableView where we have the model of the car in one column, followed by the manufacturer, followed by the year. Binding the model and year are no problem, but if I bind the relationship to a column in the table, I get the text of a relationship fault error in each cell in that column instead of anything I'm looking for. How can I play with the binding to allow me to display the proper manufacturer name associated with the car?
Extending the question a bit further, how could I set up another table view to display, say, other Car entries with the same manufacturer relationship?
A bit more information about how you have it set up currently would be helpful. You should be able to bind to your Array Controller in exactly the same fashion as your other attributes, with the same binding and controller key. Just use the full key path manufacturer.name for the model key path.
For a to-many relationship, you use two array controllers. Set up the 'master' array controller to prepare its own content from your Core Data Manufacturer class (in Entity mode). Then, you create a secondary, 'detail' array controller. Leave the detail array controller in Class mode (with the default NSMutableDictionary class), and bind its content set to your master array controller, with the controller key set to selection and the model key path to cars.
Many many tutorials exist out there that do exactly this. I highly recommend running through one or two; I found this MacResearch.org tutorial particularly helpful. The entire series is great.