Cocoa Bindings for hierarchical model

2020-03-27 10:33发布

问题:

I have a NSCollectionView based master-detail interface, where I want to display Boards in the master and Lists+Cards in the detail view.

Board, holds a NSMutableArray property lists of type List List, holds a NSArray property cards of type Card Card, has a NSString property name

The relationship is thus Board --> to-many List --> to-many Card

The master interface is fine.

The detail interface gets populated with corresponding Lists' titles for a Board. Within the detail interface I also want to populate a NSPopupButton with the cards for every list.

Problem: the NSPopupButton is empty.

Output: [<__NSArrayI 0x60000007b240> addObserver:forKeyPath:options:context:] is not supported. Key path: name

So after reading KVO, KVC and the Bindings documentation I am not sure if I need to do manual KVO for this sort of hierarchical model. Also the output hints that the name property is not KVC/KVO compliant, but it's just a NSString?

Do you suggest using an NSTreeController for this?

Bindings are setup like so:

BoardArrayController -> bound to File's owner

  • ** Model key path: boards

ListArrayController -> bound to BoardArrayController

  • ** Controller key: arrangedObjects
  • ** Model key path: lists
  • ** Mode: Class

CardArrayController -> bound to ListArrayController

  • ** Controller key: arranged Objects
  • ** Model key path: cards
  • ** Mode: Class

The NSPopupButton has

  • Controller key for Content: arrangedObjects
  • Controller key for Content Value: arrangedObjects and model key path: name

Suggestions please

回答1:

If I understand properly, in the master interface, the user selects a Board. Then, the detail interface should show the selected Board's lists. If so, the ListArrayController should be bound to BoardArrayController, controller key selection (not arrangedObjects), model key path lists.

Similarly, the CardArrayController should be bound to ListArrayController, controller key selection, model key path cards. Although it's not clear to me if the user has to first select a List and then sees a pop-up with that List's cards or if the pop-up is present in each item in the second collection view. If that's the case, then you'll need a separate array controller for each item, which is easiest if the item view is in a separate NIB.



回答2:

If each list is the representedObject for each view item in the collection view, then you can populate each popupButton with a readonly NSArray property dependent upon the cards array that is in each list. In the List class add arrangedCards as a property.

- (NSArray *)arrangedCards
{
    return [[self valueForKey:@"cards"] sortedArrayUsingDescriptors:
      [self arrangedCardsSortDescriptors]];
} 

Use the sort you want for the popup. This arranges by name.

- (NSArray *)arrangedCardsSortDescriptors
{
    NSSortDescriptor *sortByName = [[NSSortDescriptor alloc] initWithKey:
      @"name" ascending:YES];
    return @[sortByName];
}

Bind the Content of the popup to the NSCollectionViewItem.

Model Key Path is representedObject.arrangedCards.

Use representedObject.arrangedCards.name as the Content Values.