You need to bind to an NSArrayController
, not just an NSArray
because you almost certainly want to bind through the array elements to their properties and NSArray
does not support that. Bindings is built on top of Key-Value Observing and you can't key-value observe through arrays or sets.
Also, an array controller is usually part of the controller layer in Model-View-Controller. So, it doesn't make sense that "contacts" could be a key path naming a property of myViewModel
and also be an array controller. It should be a property of the view or window controller.
If your table view is NSCell
-based, you should usually bind the table columns rather than the table view itself. The table view will automatically bind a few of its own bindings based on the bindings of its columns. You would only explicitly bind the table view's bindings if you want to disable that automatic behavior.
So, you might do, for each column:
[tableColumn bind:NSValueBinding toObject:self.contactsArrayController withKeyPath:@"arrangedObjects.propertyAppropriateToColumn" options:nil];
If your table view is view-based, you should bind the table view's bindings and not the columns. So, you would do:
[contactsTableView bind:NSContentBinding toObject:self.contactsArrayController withKeyPath:@"arrangedObjects" options:nil];
[contactsTableView bind:NSSelectionIndexesBinding toObject:self.contactsArrayController withKeyPath:@"selectionIndexes" options:nil];
[contactsTableView bind:NSSortDescriptorsBinding toObject:self.contactsArrayController withKeyPath:@"sortDescriptors" options:nil];
If your table cell view is NSTableCellView
or a subclass, then the table view will set its objectValue
property to the corresponding element from the array. The views within the NSTableCellView
should bind to it, using a key path like objectValue.propertyAppropriateToThatView
. If you're not using NIBs, you're going to have to set up this binding in your -tableView:viewForTableColumn:row:
delegate method. You also have to tear down the bindings, which is going to be kind of hard because the table view doesn't tell you when it discards a cell view. It might work to do the setup in -tableView:didAddRowView:forRow:
and teardown in -tableView:didRemoveRowView:forRow:
, but those don't directly give you the cell views. You'll need to correlate the cell views within the row view to the table columns yourself. (Not hard, just tedious.)
If your table cell view is a control or other view which responds to -setObjectValue:
, then the table view will call that to set the object value to the element of the array. In that case, you may want to change the content binding to go through arrangedObjects
to the relevant property of array elements.
The situation with a collection view is similar to a view-based table view. You'd only bind NSContentBinding
and NSSelectionIndexesBinding
; there's no NSSortDescriptorsBinding
because collection view's don't have an interface for the user to change the sorting (like table column headers). The collection view will set each collection view item's representedObject
to the element of the array. The item view or its subviews should bind to the item, through representedObject
to some specific property of the element.
If you aren't using NIBs, you'll probably want to use a custom subclass of NSCollectionViewItem
. That will set up the bindings in its -viewDidLoad
method (10.10 or later) or its -loadView
method after calling through to super. You can tear down the bindings when it deallocates. Alternatively, you can set them up and tear them down in -viewWillAppear
and -viewWillDisappear
.