Search code examples
objective-ccocoanstableviewcocoa-bindingsnsarraycontroller

My first Cocoa master-detail application: Binding difficulties


I'm writing my first master detail view in Cocoa. My data model is really simple: just an NSMutableArray that contains instances of a single class having a few NSStrings properties.

Here's what I've created so far:

  • A class representing the instances.
  • An NSMutableArray specified as a property of my app delegate to hold the class instances.
  • A master detail view, with an NSTable and some text fields to hold the properties of a selection instance.
  • An NSArrayController with a binding specifying the app delegate, and the name of the NSMutableArray property as the model key path.
  • Bindings between the columns of the NSTable and the NSArrayController (controller key = "arrangedObjects", and model key path of each column = a property of the class).
  • Bindings between the text fields of the view and the selection (controller key = "selection", and model key path of each text field = a property of the class).
  • A "+" button and a "-" button in the view to enable adding and removing objects.

However, I'm having two problems with this design:

(1) I can't find a good way to implement the "+" and "-" buttons. First, I bound them to the add: and remove: properties of the array controller. While this seems to work, it has a problem: my class declares an init member that initializes the NSStrings to stub values, but the array controller doesn't seem to [init] the new instances, because the new list entry has empty strings for each column.

Next, I attached them to IBActions in my app delegate that added or removed an object from the NSMutableArray. However, this feels wrong - it feels like I'm violating the model-view-controller architecture by not talking to the array controller. For example, the "-" function has to talk to the array controller to get the selected item. Also, I notice that I have to send a [didChangeValueForKey] message after altering the array - which feels like a signal that I'm doing this wrong.

(2) One of the detail subviews in my view is an NSTextView that's bound to an NSString in the selected object. It really isn't working as expected: when new text is entered, the text remains the same even if other members of the class (which should have different values) are selected. Also, the text data isn't being saved to any instance of the class.

Any thoughts? Thanks in advance.


Solution

  • Here is an example that should be close to what you want:

    #import "AppDelegate.h"
    #import "Members.h"
    
    @implementation AppDelegate
    
    - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
        self.theData = [NSMutableArray arrayWithObject:[[Members alloc] init]];
    }
    
    -(IBAction)addMember:(id)sender {
        [self.controller addObject:[[Members alloc] init]];
    }
    
    -(IBAction)removeMember:(id)sender {
        [self.controller removeObjectAtArrangedObjectIndex:self.controller.selectionIndex];
    }
    

    The array controller (whose IBOutlet is controller) has its content array bound to theData. Members is the name of my class that has 3 string properties, name, author and blurb. In the detail view, the 2 text field's are bound to ArrayController.selection.name or author, just like you said you did. The other property, blurb, is bound the same way (ArrayController.selection.blurb), but to the Attributed String value of a text view. This worked fine, I'm not sure what your trouble with the text view was. If I add text to the text view, it does show up there if I click another row in the mater table and then click back (also if I log the array, it shows up there too).