How do I bind my Array Controller to my core data model?
In a storyboard based project there is no (binding) reference from a view controller to the AppDelegate
class.
A solution is to add a property and override init?(coder
in the view controller
@objc let managedObjectContext: NSManagedObjectContext
required init?(coder: NSCoder) {
self.managedObjectContext = (NSApp.delegate as! AppDelegate).persistentContainer.viewContext
super.init(coder: coder)
}
then bind the ManagedObjectContext
to ViewController
-> managedObjectContext
.
In the Attribute Inspector of the array controller don't forget to set the Mode
to Entity Name
, insert the entity name and check Prepares Content
.
What is the new way of binding an NSArrayController to the managed object context of a Core Data document?
So I have the answer from Apple. This is for Document based Core Data apps, the code is all in Swift but the idea is the same in Objective-C you just have to translate it.
The first answer they gave me was to bind the array controller to the view controller running the view with a model key path of self.view.window.windowController.document.managedObjectContex
. The sample I was shown used this method and had no error messages at all, it was however a single view controller inside the window controller with one array controller. My setup is a window to a tab view to the views with two array controllers in the one scene. I was still getting Cannot perform operation without a managed object context
once each time a new document was opened or created. The second solution, that worked for me was to still bind the array controller to the view controller but with a model key path of self.representedObject.managedObjectContext
and then to add to the end of the document class's makeWindowControllers()
function:
override func makeWindowControllers() {
……
let tabViewController = windowController.contentViewController as NSTabViewController
for object in tabViewController.childViewControllers {
let childViewController = object as NSViewController
childViewController.representedObject = self
}
}
This solved the issue for me. Hopefully there is enough info here to show up when others google this issue.
What Happened to Core Data Bindings?
Control-drag:
Control-drag to the property, configure the binding and click on Connect.
Bindings Inspector:
Configure the binding and turn on the Bind to checkbox.
How do you bind a storyboard view to a Core Data entity when using NSDocument?
Steps to create a sample Xcode Document-Based Application project with Core Data, Storyboard, NSArrayController, NSTableView and Bindings.
Step 1 Create a Xcode project. Choose OS X Cocoa Application and select ‘Use Storyboards’, ‘Create Document-Based Application’ and ‘Use Core Data’.
Step 2 Select the data model. Add entity ‘Person’ and string attributes ‘name’ and ‘address’.
Step 3 Select Main.storyboard. Add a NSArrayController to the view controller scene. Set Mode to ‘Entity Name’ and set Entity Name to ‘Person’. Check ‘Prepares Content’. Bind Managed Object Context
of the array controller to View Controller
, Model Key Path representedObject.managedObjectContext
.
Step 4 Go to the view of the view controller scene. Remove ‘Your document contents here’. Add a NSTableView. Bind Content
to Array Controller
, Controller Key arrangedObjects
. Bind Selection Indexes
to Array Controller
, Controller Key selectionIndexes
. Bind Sort Descriptors
to Array Controller
, Controller Key sortDescriptors
.
Step 5 Bind Value
of the text fields in the table view to Table Cell View
, Model Key Path objectValue.name
and objectValue.address
. Check 'Conditionally Sets Editable'.
Step 6 Add two Push Buttons ‘Add’ and ‘Remove’ to the view of the view controller scene. Connect the actions to actions add:
and remove:
of the array controller.
Step 7 (Objective-C) Select Document.h. In method makeWindowControllers
, replace statement [self addWindowController:…
by
NSWindowController *aWindowController = [[NSStoryboard storyboardWithName:@"Main" bundle:nil] instantiateControllerWithIdentifier:@"Document Window Controller"];
[self addWindowController:aWindowController];
aWindowController.contentViewController.representedObject = aWindowController.document;
Step 7 (Swift) Select Document.swift. In method makeWindowControllers
, at the end after self.addWindowController(windowController)
add
windowController.contentViewController!.representedObject = windowController.document
Step 8 Build, Run, Test.
ArrayController's CoreData selection binding not refreshed across multiple NIB files
It seems you're missing the fact that, when you create the second array controller on the Details.xib it has no relation to the array controller on the MainMenu.xib. They are two separate instances.
When you change the selection on the PopUp the only array controller affected is the one on MainMenu.xib.
You have several options here:
- When you create your DetailViewController pass a reference to the array controller on the Controller and bind to that (don't create a new one on the details.xib)
- Just use simple KVO to observe the selection on Controller, and programatically change your label value.
- Just use simple KVO to observe the selection on Controller and update the array controller on the DetailsViewController to keep them in sync.
- your solution here...
As long as you understand what's going on I'm sure you'll find the best solution to your original problem.
Problems with cocoa bindings w/ master-detail where the detail contains an array
The content set of the Chapters array controller is bound to the selection of the Books array controller. When you select a row in the Books table view, the Books array controller's selection has to be synchronized by binding the selections indexes. I usually bind Content to arrangedObjects
, Selection Indexes to selectionIndexes
and Sort Descriptors to sortDescriptors
.
The bindings from the Chapters table view are the same as the Books table view. Bind content of the Chapters table view to arranged objects of the Chapters array controller.
Using an NSArrayController in Multiple Storyboard Scenes
The key to making this work is to have a NSArrayController
instance in each of your descending NSViewController
subclasses and binding them together through a central data source (most likely your NSDocument subclass). You can then set this data source as your NSViewController
subclasses representedObject
by passing it down through your descending
controllers. Here is an example of a storyboard application with an NSWindowController
which has a content view controller that is a NSSplitViewController
with two child view controllers (A Master / Detail setup):
class Document: NSDocument {
var dataSource: DataSource? = DataSource()
...
}
class DataSource: NSObject, NSCoding {
var items: [Item] = []
var selectionIndexes: NSIndexSet = NSIndexSet()
...
}
class WindowController: NSWindowController {
override var document: AnyObject? {
didSet {
if let document = self.document as? Document {
self.contentViewController?.representedObject = document
}
}
}
}
class SplitViewController: NSSplitViewController {
override var representedObject: AnyObject? {
didSet {
for viewController in self.childViewControllers as! [NSViewController] {
viewController.representedObject = representedObject
}
}
}
}
The trick is to bind the representedObject
to each of your descending view controller's NSArrayController
in the storyboard. You need to bind NOT ONLY the contentArray
BUT ALSO the selectionIndexes
.
The result is that the selectionIndexes
on both descending NSArrayController
s are kept in sync because they are bound through the central data source (DataSource
subclass in above example).
To make this all clearer I have created an example project that demonstrates this here: https://github.com/acwright/StoryboardBindingsExample
Using Core Data and Cocoa Bindings in multiple storyboard scenes
Instead of adding array controllers to reconstruct the selected list or player it's more straightforward to transfer the selected list and player to the other view controller. It requires some code to change the selected list and player when the selection in the table view changes.
class Controller: NSObject {
@objc dynamic var moc = …
@objc dynamic weak var selectedList : List?
@objc dynamic weak var selectedPlayer : Player?
}
Left-most List view controller:
The view controller is the delegate of the table view.
class ListViewController: NSViewController, NSTableViewDelegate {
@IBOutlet var arrayController: NSArrayController!
func tableViewSelectionDidChange(_ notification: Notification) {
let controller = representedObject as! Controller
if arrayController.selectedObjects.count == 1,
let list = arrayController.selectedObjects[0] as? List {
controller.selectedList = list
}
else {
controller.selectedList = nil
controller.selectedPlayer = nil // tableViewSelectionDidChange isn't called on the Player table view
}
}
}
Middle Player view controller:
The view controller is the delegate of the table view.
class PlayerViewController: NSViewController, NSTableViewDelegate {
@IBOutlet var arrayController: NSArrayController!
func tableViewSelectionDidChange(_ notification: Notification) {
let controller = representedObject as! Controller
if arrayController.selectedObjects.count == 1,
let player = arrayController.selectedObjects[0] as? Player {
controller.selectedPlayer = player
}
else {
controller.selectedPlayer = nil
}
}
}
Bind the Content Set of the Player array controller to View Controller.representedObject.selectedList.players
Right-most Detail view controller:
Bind the Value of the text field to View Controller.representedObject.selectedPlayer.name
Related Topics
How to Download Video Urlstring from Firebase Database Not Storage in Swift
How to Instantiate a View Controller Programatically, Without Storyboard
Post Urlrequest Doesn't Work in Swift 4
How to Make Phone Calls in Swift
Swift Nsbundle Pathforresource() Returns Nil
Can Not Import Tensorflow for Swift in Xcode Playground
How to Move an Object Towards a Direction Without Stopping
Wrong Value Returned After Formatting Timestamp
How to Insert UIlabel After UItextfield in UIalertcontroller
iOS Charts - Single Values Not Showing Swift
Can't Hide Status Bar in Avplayerviewcontroller's Portrait Mode
Image to String Using Base64 in Swift 4
How to Reinterpret_Cast in Swift
Realm Data Insertion Took Over 7Mins for Big Size JSON
How to Create a UIprintpaper to Test UIprintinteractioncontrollerdelegate
Could Not Find an Overload for '-' That Accepts The Supplied Arguments
Return Object for a Method Inside Completion Block
How to Disabled Some Default Functionality in Scene View When Allowscameracontrol = True