views:

332

answers:

3

Apple has a nice little tutorial for making a simple master-detail interface. Interface Builder will even automatically generate one for you from a Core Data entity. However, I'm trying to do something more complicated than the simple example and I've been struggling for a while to get it to work.

I have a Core Data document-based application. The model includes an abstract entity Page, and several concrete subentities of Page. All Pages have some attributes in common (like "name") and those are defined in Page. Obviously, the subentities have the attributes that are unique to them.

I want the interface to allow the user to see all types of Pages in the master list (an NSTableView). When they select a Page, the detail fields that are displayed would depend on what kind of Page it is.

Here's what I have right now:

I have a main nib file, where the master list is displayed, plus all fields common to a Page. There's a nib for each page type with its specific fields. There's the main NSArrayController in the main nib file, which is populating the NSTableView. There's an NSArrayController in each of the page-specific nibs as well so that I can bind the detail fields to attributes of the current selection. All of my NSArrayControllers are configured identically, and I have them all bound to the same managedObjectContext and the same selectionIndexes.

I am using Aaron Hillegass's method for view swapping that he describes in his Cocoa book. So I registered for NSTableViewSelectionDidChangeNotifications, and when I receive one, it calls a method switchView:

switchView looks at the currently selected object, checks which type of Page it is, and swaps in the appropriate nib file according to Hillegass's method.

Everything works fine if I only add pages of one type, but as soon as I add a page of a second type, I get this error:

Error setting value for key path selectionIndexes of object (from bound object entity: Page, number of selected objects: 1): [ valueForUndefinedKey:]: the entity NoColPage is not key value coding-compliant for the key side.

The last part of the error makes sense: it's stuck trying to display the wrong nib, so it's trying to bind to fields that don't exist for this object.

I added a selectionIndexes field to MyDocument so that all of my NSArrayControllers could bind to the same place. I've agonized over this for days and I can't figure it out. Any ideas?

If it helps, here is a sample project you can download. I extracted only the things relevant to this problem from my project into a new dummy application, which I've been using to test and play around.

PS: Interface Builder's tool for generating a master-detail interface from a Core Data entity doesn't work like I want it to for abstract entities. It only creates fields for the attributes in the superentitity.

Edit: I think Joshua is on to something, but unfortunately, it doesn't work--I keep running into the same problem. At first I was having a hard time because I didn't understand that -unbind: expects a string constant, not a key path.

I've tried several variations: where I keep track of the currently displayed nib's array controller; where I keep track of the currently displayed page type and only unbind/rebind when I'm trying to display a different page type...

Here's the relevant section of code.

-(void) displayViewController: (ManagingVC *) vc withClass:(NSString*) className {

//Try to end editing
NSWindow *w = [box window];
BOOL ended = [w makeFirstResponder:w];
if (!ended) {
    NSBeep();
    return;
}

//The Managing View Controller's NSArrayController
NSArrayController* vcAc = [vc arrCont];

//if the page we're trying to switch to is NOT the same type as the current page we're displaying
//if it is, do nothing.
if (![currPageDisplayed isEqual:className]) {

 //unbind the old view controller
 ManagingVC *oldvc = [viewControllers objectForKey:className];
 NSArrayController* oldsac = [oldvc arrCont];
 [oldsac unbind:@"selectionIndexes"];

 //bind the new view controller
 [vcAc bind:@"selectionIndexes" toObject:self withKeyPath:@"selectionIndexes" options:nil];
 currPageDisplayed = className;

 NSView *v = [vc view];

 //display the new view in an NSBox in the main nib
 [box setContentView:v];
}

}

+1  A: 

The problem is you're leaving your nibs' array controllers bound to the document's selection, which is causing them to attempt (when the selection changes) to represent the selected item.

Try this:

  1. Remove the bindings in the nibs.
  2. Before adding a new view, connect its bindings in code.
  3. Before removing the old view, disconnect its bindings in code.

This should keep everything happy.

Joshua Nozzi
Thanks! I think you're on to something, but it's still doing the same thing. I added more information to the original question post.
Ellie P.
Where / how are you getting *className*? This is important. Have you stepped through this code to see if it's actually behaving as expected?
Joshua Nozzi
className isn't the issue, and yep, I've stepped through and everything is behaving as expected. But it still refuses to work. The only solution I've been able to find is programmatically controlling which GUI elements are displayed in the main NIB (thus eliminating the multiple NIBs.) It's not pretty, but it will have to do for now.
Ellie P.
A: 

After spending way too much time trying to get each NIB's array controller in sync, I've given up on that approach. The only thing that has worked for me is programmatically controlling which GUI elements are displayed in the main nib, and their bindings. This means eliminating the other nibs. This isn't really a sustainable solution if you're working with more than just a couple of text fields, but it works for me right now.

I still follow Joshua's advice of unbinding before switching the view, but right now I'm only binding the text fields to arrayController.selection.whateverKey

Ellie P.
A: 

I need this example of project ,i have the same problem, you can post the new project ,source (solution) please !! thanks by advance