Accessing a View Controller in a Show Segue

I have a document-based Mac application that uses storyboards. The document window has a split view controller. My application creates PDF files, and I want to preview the document's PDF in my application. In the storyboard I create a window controller and a view controller for the preview window. In the document window's toolbar I have a Preview item. I create a segue to show the preview window when the Preview item is clicked.

The preview window's view controller needs to access the split view controller so it can get the document to display the PDF in the preview window. My first attempt was to use the presentingViewController property in viewDidLoad() to access the split view controller, but presentingViewController was nil.


My next attempt was to subclass NSViewController and NSWindowController and implement prepareForSegue(), with the split view controller being the segue's source view controller. But prepareForSegue() doesn't get called. I made sure to set the custom class of the preview window controller and view controller to my subclasses in Interface Builder.


How do I access the split view controller from the preview window if presentingViewController is nil and prepareForSegue() doesn't get called?

The best way to do this would be to set the "representedObject" of the Preview Window's Content View Controller via the segue.

An example would where you call the segue to show/modal show the window, I've passed a ref of the Document to the callingViewController below


document ref being passed:

class Document: NSDocument {
...
    override func makeWindowControllers() {
        /
        let storyboard = NSStoryboard(name: "Main", bundle: nil)
        let windowController = storyboard.instantiateControllerWithIdentifier("Document Window Controller") as! NSWindowController


        windowController.contentViewController?.representedObject = self


        self.addWindowController(windowController)
    }
...
}


callingViewController would be the contentViewController of the NSWindiow which the NSDocument above creates.

class callingViewController: NSViewController {
     ...
    override var representedObject: AnyObject? {
        willSet{
            let representedDocument = newValue as! Document
            self.testValue = representedDocument.testValue
        }
    }
     ...
}


Also in teh callingViewController class, you need to override "prepareForSegue". This was emphasied when Storyboards for Cocoa were frost shown on WWDC a few years back


    override func prepareForSegue(segue: NSStoryboardSegue, sender: AnyObject?) {
        if (segue.identifier == "showPreview"){
          
            assert(representedObject != nil, "The Document wasn't passed to the Main Window Content View Controller Properly, unable to sent anything via the show Preview Window segue")
          
            let previewWindowController = segue.destinationController as! NSWindowController
            previewWindowController.contentViewController?.representedObject = self.representedObject
        }
    }


Note in the above, yet another "representedObject is being set" and this is the contentViewController of the popup Preview Window. )note that the segue identifier is being cheched, so needs to be set in IB or in your code.


and Finally, in the PreviewWindow's ContentViewController:


    override var representedObject: AnyObject? {
        willSet{
            self.representedDocumentVar = newValue as! Document
        }
    }


And so when you get the value, you set what you need to set as above...


Hope this helps... it took me a while to get my head wrapped around!


Regards,


Ade

Your reply was very helpful, but I still have a problem. I override prepareForSegue() in my split view controller, which is the same as CallingViewController in your example. But prepareForSegue() doesn't get called. I set a breakpoint at the if statement in prepareForSegue(), but the breakpoint is never hit. I set the identifier for the show segue to showPreview in Interface Builder.

I'm having the same issue. prepareForSegue is never callled (OS X, Obj-c).


Here is the callstack

#0
0x0000000100001cb4 in -[ViewController viewDidLoad] at /Users/mtozer/code/mycode/Apps/ArchitectureInspector/ArchitectureInspector/ViewController.m:14
#1
0x00007fff86e7fc35 in -[NSViewController _sendViewDidLoad] ()
#2
0x00007fff86e7691b in -[NSViewController _loadViewIfRequired] ()
#3
0x00007fff86e76771 in -[NSViewController view] ()
#4
0x00007fff877f55a3 in -[NSTabViewController _goodTabViewContentSize] ()
#5
0x00007fff877f587f in -[NSTabViewController viewDidLoad] ()
#6
0x0000000100001ad6 in -[MTTabViewController viewDidLoad] at /Users/mtozer/code/mycode/Apps/ArchitectureInspector/ArchitectureInspector/MTTabViewController.m:18
#7
0x00007fff86e7fc35 in -[NSViewController _sendViewDidLoad] ()
#8
0x00007fff86e7691b in -[NSViewController _loadViewIfRequired] ()
#9
0x00007fff86e76771 in -[NSViewController view] ()
#10
0x00007fff87020708 in -[NSWindow _contentViewControllerChanged] ()
#11
0x00007fff9290e7a4 in -[NSObject(NSKeyValueCoding) setValue:forKey:] ()
#12
0x00007fff8706a52a in -[NSWindow setValue:forKey:] ()
#13
0x00007fff8706a48f in -[NSIBUserDefinedRuntimeAttributesConnector establishConnection] ()
#14
0x00007fff86df9bcc in -[NSIBObjectData nibInstantiateWithOwner:options:topLevelObjects:] ()
#15
0x00007fff86f0a40b in -[NSNib _instantiateNibWithExternalNameTable:options:] ()
#16
0x00007fff86f0a05b in -[NSNib _instantiateWithOwner:options:topLevelObjects:] ()
#17
0x00007fff8756c376 in -[NSStoryboard instantiateControllerWithIdentifier:] ()
#18
0x00000001000015b2 in -[Document makeWindowControllers] at /Users/mtozer/code/mycode/Apps/ArchitectureInspector/ArchitectureInspector/Document.m:41
#19
0x00007fff87740700 in -[NSDocument(NSPersistentUISupport) restoreDocumentWindowWithIdentifier:state:completionHandler:] ()
#20
0x00007fff8737b93b in -[NSDocumentControllerPersistentRestoration loadedDocument:forAutoID:] ()
#21
0x00007fff8738077d in __89-[NSDocumentController reopenDocumentForURL:withContentsOfURL:display:completionHandler:]_block_invoke_2 ()
#22
0x00007fff8c87724c in __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ ()
#23
0x00007fff8c856b65 in __CFRunLoopDoBlocks ()
#24
0x00007fff8c8568cb in __CFRunLoopRun ()
#25
0x00007fff8c855d38 in CFRunLoopRunSpecific ()
#26
0x00007fff91b30d55 in RunCurrentEventLoopInMode ()
#27
0x00007fff91b30a97 in ReceiveNextEventCommon ()
#28
0x00007fff91b309cf in _BlockUntilNextEventMatchingListInModeWithFilter ()
#29
0x00007fff86e2df3a in _DPSNextEvent ()
#30
0x00007fff86e2d369 in -[NSApplication _nextEventMatchingEventMask:untilDate:inMode:dequeue:] ()
#31
0x00007fff86e21ecc in -[NSApplication run] ()
#32
0x00007fff86deb162 in NSApplicationMain ()
#33
0x0000000100002842 in main at /Users/mtozer/code/mycode/Apps/ArchitectureInspector/ArchitectureInspector/main.m:12
#34
0x00007fff8e1ab5ad in start ()


I have


- (void)prepareForSegue:(NSStoryboardSegue *)segue sender:(id)sender
{
  [super prepareForSegue:segue sender:sender];
}


On each window and view controller subclass and have correctly set the classes in IB (the viewDidLoad methods are called)

I think I have worked out why

Although in interface builder it looks as though the relationship is a segue, it is apparently not treated as such. It is a 'relationship' type and is made through containment (child / parent view controllers). Therefore I was able to pass down the represetedObject (the document) from my window to its children by implementing the following in my subclasses.


- (void)setRepresentedObject:(id)representedObject {
  [super setRepresentedObject:representedObject];
  self.document = representedObject;
  for (NSViewController *viewController in self.childViewControllers) {
    viewController.representedObject = representedObject;
  }
}
Accepted Answer

I figured out the problem. The source and destination controllers in a show segue are window controllers, not view controllers.


The solution is to implement prepareForSegue() in the document window's window controller. I can access the split view controller using NSWindowController's contentViewController property. Use the segue's destinationController property to access the preview window's window controller, and use contentViewController to access the preview window's view controller.

Accessing a View Controller in a Show Segue
 
 
Q