In my first guide to binding, it uses NSFetchedResultsController
, which is tightly coupled to the Core Data framework.
It is not necessary (of course) to bind to Core Data models.
This guide will be more generic, providing a guide to binding to a plain old object model.
The Model
Let’s use a simple model MyData
.
class MyData: NSObject {
@objc var name: String
}
Any member variable such as name
has to be annotated with @objc
, because that’s how the old world works and provides.
Array Controller
NSArrayController
manages the models.
It does NOT hold the models, but simply manages it in terms of sorting, filtering, and CRUD operations.
It is common to add the NSArrayController
object in a storyboard. Then set up as such:
- In Attributes Inspector:
- Mode is class
- Class name is
MyApp.MyData
- The prefix
MyApp
is necessary, and it is your target’s Product Module Name
- In Binding Inspector > Controller Content:
- Bind to your view controller (usually) eg.
MyListViewController
- Model Key Path is
self.myDataList
- Bind to your view controller (usually) eg.
View Controller
It is a good time now to show you what your view controller looks like.
class MyListViewController: NSViewController {
@objc dynamic var myDataList = [MyData]()
@objc dynamic var sorts = [NSSortDescriptor]()
@objc dynamic var filter: NSPredicate?
}
myDataList
is the actual models that the view controller holds. It must be prefix with @objc
once again.
Remember: The NSArrayController
is managing these models. You don’t mutate the array controller directly.
Whenever you want to update your model, mutate myDataList
.
Table View
Next, we bind the array controller to the table view.
- In Attributes inspector, set Content Mode to View Based.
- In Bindings inspector, bind Content to Array Controller. The Controller Key should already be
arrangedObjects
. - Select the actual view to bind eg.
NSTextField
,NSDatePicker
etc - In Bindings inspector, bind Value to Table Cell View
- Set the Model Key Path to
objectValue.name
(your model’s attribute!)
Sort & Filter
- Select Array Controller > Bindings inspector > Sort Descriptors > Bind to the view controller
- Set Model Key Path to
self.sorts
- Similarly, for filter bind to
self.filter
- Init
sorts
andfilter
inviewDidLoad
- Mutate them, and the views will be updated immediately
Sort when clicking on the table column header
- Select Table View > Bindings inspector > Sort Descriptors > Bind to the view controller
- Set Model Key Path to
self.sorts
- Select a table column > Bindings inspector > Value > Bind to Array Controller
- Set the model key path eg. “name”
- Make sure Creates Sort Descriptor is enabled
Selection
When you select rows in the table view, you should bind it to the array controller too.
- Select Table View > Bindings inspector > Selection Indexes > Bind to the Array Controller
- Set Controller Key to
selectionIndexes
Delete Selected Rows
@IBAction func deleteSelectedRows(_ sender: Any) {
arrayController.remove(atArrangedObjectIndexes: arrayController.selectionIndexes)
}
I have omitted the obvious: Add a button with the action to the above function, adding the array controller outlet to the view controller.
Add New Row
Similarly, you can have a button to add new model(s).
@IBAction func add(_ sender: Any) {
let newModel = MyData()
arrayController.addObject(newModel)
}