wolfteeth wolfteeth - 11 days ago 8
Objective-C Question

Set representedObject on an NSViewController in a document-based OS X application

I am working on a simple OS X Cocoa application, in which the user fills in a form and the application performs some calculations that pop up in other fields.

I started with the default Document-Based Application template on Xcode 6.3.2 (for Yosemite). This template includes a subclass of NSDocument and a subclass of NSViewController. The interface is defined in a Storyboard. The NSDocument initializes the interface from the Storyboard in makeWindowControllers.

I ran into trouble binding pieces of the interface to the ViewController's representedObject -- it turned out I was binding to nil. The ViewController's setRepresentedObject method is never called.

I tried having the ViewController call setRepresentedObject on itself in viewDidLoad, but the WindowController's document property hadn't been set at that point (it returned nil).

And it's not clear to me if it's possible to get an instance of the ViewController from the NSDocument - the NSDocument knows about its window/WindowController, but I'm not sure how to find a ViewController from those classes.

I want to know the idiomatic Xcode/Apple way to do this. Whether that is via the Storyboard view or programmatically in my code.

I would also like to avoid coupling the code too tightly, if possible.

Answer

I was able to set the view controller's represented object by overriding the NSViewController's viewWillAppear method, which appears to be called after the document instance is set as the window document.

- (void)viewWillAppear {
    [super viewWillAppear];

    // Set up the document as the data source
    NSLog(@"viewWillAppear");
    NSWindow *myWindow = [[self view] window];
    NSWindowController *myWindowController = [myWindow windowController];
    CharacterSheetDocument *myDocument = [myWindowController document];
    [self setRepresentedObject: [myDocument characterData]];
}

I do not know if this is the cleanest way to do this, but it does work.

It seems a little clunky for a view controller to not only walk through a nested series of getters to find the model object, but know all about the document subclass.