Binding selection across multiple view controllers

2019-02-15 16:53发布

问题:

I am having an issue wrapping my head around how to hook up a few NSArrayControllers across two view controllers. I want to sync the selection in the source list table view to update the values in the second detail view controller.

I'm using the Cocoa Dev Central Build A Core Data Tutorial as the starting point, but have broke down the architecture so that there is an NSWindowController that contains two NSViewControllers: one for the posts table on the left and one for the post details on the right.

The NSWindowController subclass has an NSArrayController that is bound to the Post entity and a read-only managedObjectContext accessor that points to [[NSApp delegate] managedObjectContext]

I then initializing the two view controllers in the windowDidLoad method.

- (void)windowDidLoad
{
  static NSInteger kSourceListViewIndex = 0;
  static NSInteger kDetailViewIndex = 1;

  self.postsListsViewController = [[MDVCPostsListViewController alloc] initWithWindowController:self];  
    NSView *sourceListSplitViewContentView = [[self.splitView subviews] objectAtIndex:kSourceListViewIndex];
  NSView *sourceListView = [self.postsListsViewController view];
  [sourceListView setFrame:[sourceListSplitViewContentView bounds]];
  [sourceListView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
  [sourceListSplitViewContentView addSubview:sourceListView]; 

  // And now let's load the detail view.
  self.postDetailViewController = [[MDVCPostDetailViewController alloc] initWithWindowController:self];
    NSView *detailSplitViewContentView = [[self.splitView subviews] objectAtIndex:kDetailViewIndex];
  NSView *detailView = [self.postDetailViewController view];
  [detailView setFrame:[detailSplitViewContentView bounds]];
  [detailView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
  [detailSplitViewContentView addSubview:detailView]; 
}

MDVCPostsListViewController has an NSArrayController bound to the Post entity and its managed object context bound to the parent window controller's managed object context (all through Interface Builder)

MDVCPostDetailViewController has an NSObjectController bound to the window controller's managed object context and the content object bound to the window controller via postsListsViewController.postsArrayController.selection. This seems like a really sucky hack.

How can I get it so that changing the selection in MDVCPostsListViewController's table view will update the selected values in MDVCPostDetailViewController? I feel like I'm close, but am not sure what's missing or what is the best route to take. I do feel that the postsListsViewController.postsArrayController.selection binding is extremely hacky. Hopefully there's a better way.

I've uploaded my sample project that exhibits this for those that prefer to look at code rather than just read descriptions. You can grab it from my site at http://www.secondgearsoftware.com/attachments/stackoverflow_objectcontroller.zip

回答1:

How do you expect each window controller to discover the main window controller?

From a quick glance at the project, it seems you just added an instance of MDVCMainWindowController to each NIB. These will be separate instances from the one actually running the main window.

You need to bind your source list table selectionIndexes to the the Post array controller. Otherwise the selection will not be known at the controller level.

I would suggest moving the array controller up into the main window controller. In your windowDidLoad method you could then pass it down to both the list and detail view. The list would bind to arrangedObjects and selectionIndexes, the detail view would bind to selection.someKey.



回答2:

The problem is with the NSObjectController instance's content object binding in the detail view xib. Your array controller in the list view xib is correctly posting KVO notifications (checked this by making the MDVCPostDetailViewController instance observe this with KVO), so I'm not sure why, but for some reason the object controller isn't responding to them.

However, there are a couple of different approaches you can take that will work. You can get rid of the object controller and bind the text fields, etc, in the detail view xib directly to the array controller (File's Owner.windowController.postsListsViewController.postsArrayController.selection.whatever). Alternatively you can keep the object controller and have your detail view controller observe the selection property of the array controller and 'manually' set the object controller's content object property when the selection changes.