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

3.2k views Asked by At

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.

2

There are 2 answers

1
wolfteeth On BEST 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.

0
stko On

I was just looking for how to do this myself when I ran across this question. Doing this in the viewWillAppear method of your ViewController class (as suggested by wolfteeth) makes the ViewController dependent on the Document object, which seems backwards to me.

Might be better to do this in the makeWindowControllers method of the Document class, like so:

override func makeWindowControllers() {
    // Returns the Storyboard that contains your Document window.
    let storyboard = NSStoryboard(name: "Main", bundle: nil)
    let windowController = storyboard.instantiateController(withIdentifier: "Document Window Controller") as! NSWindowController
    self.addWindowController(windowController)

    // Set represented object of ViewController
    if let viewController: ViewController = windowController.contentViewController as! ViewController? {
        viewController.representedObject = self.model
    }
}

This code snippet assumes that you have a property in your Document class named "model" that references the model object that your ViewController represents.