Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSToolbar in Xcode 7 using Storyboards (NSWindowController -> NSSplitViewController)

Hi I've seen this question asked a few times already but with no definite answer yet so I created it for xcode 7 and swift2 (which may have changed things a bit anyway).

I created a project using Xcode 7 and Cocoa OSX Story boards + swift2, so my project started with a NSWindowController that Connects to a NSViewController (as expected!). I added a NSToolbar to my window controller and added a NSButton to the toolbar. I changed my NSViewController to be one of the new NSSplitViewController that links to three NSViewControllers and displays their views horizontally - with vertical dividers - (similar to the layout you see in the photo app or pages in Yosemite +). My final goal will be that the button in My toolbar shows and hides the first split.

It is my understanding is, and I would expect that to achieve this I should create an action in the NSSplitViewController that changes the auto layout constrains more or less in the way they are working it out here: How to do collapse and expand view in mac application?.

And then somehow link this action to the NSButton that is in the Toolbar... which happens to be in the NSWindowController (far up and isolated in the hierarchy)...

I have already gone through other questions about NSToolbar and storyboards and failed to accomplish my goal:

  • The YouTube video: Cocoa Programming L17 - NSToolbar which is the closest I found to solve the problem, but his method does not work for storyboards, only creating your own xib file.
  • In this question: How to use NSToolBar in Xcode 6 and Storyboard? One person proposes to make the link using the first reponder and expecting everything to hook up at run-time (which looks a bit dodgy and not the way apple would implement it I think...). A second person suggested to create a view controller variable in the NSWindowController and manipulate its properties from there... but again, a bit dodgy too.

  • One latest comment I saw in that question which seems the best way to tackle the problem (but still not as good as I guess it could be) is to add a NSObjectController to the dock of each scene and when the scene loads, set the values of the objects to the other secene's controller. Is this really the best way to go ahead? If so, how could I achieve this one?

Apple did mention (again) in WWDC15 that they created storyboards for osx and the split-view controller that owns view-controllers so that you can move your logic and work to the specific view-controller, so I would be expecting to do everything from inside my split-view controller as this is the target that needs to change.

Does anyone know how to achieve this from the view controller itself? I really haven't been able to find a way to connect my ToolBarItem to it.

like image 277
gbdavid Avatar asked Jun 24 '15 10:06

gbdavid


2 Answers

OK, I've created this question quite a few days ago and no answer so far so I've answer with what I recently did to overcome the problem.

After I created my Xcode project I did this:

  • Created a subclass MySplitViewController for the NSSplitViewController
  • Added an IBOutlet for each NSSplitViewItem. For example:

    @IBOutlet weak var mySplitViewItem: NSSplitViewItem!

  • Created a subclass WindowController for the NSWindowController

  • Added an IBAction in the WindowController class that links to the NSToolbarItem (my button)
  • Added a property that gets the Window Controller's content as MySplitViewController

    var mySplitViewController: MySplitViewController { return self.window?.contentViewController as! MySplitViewController }

  • Now I can access the split view controller's property from the Window Controller in the action I created:

    mySplitViewController. mySplitViewItem.collapsed = true

I created some sample code that does this (but using a view controller and changing the text for a label here, just in case someone wants to see a working project with this behaviour. And a blog post about it too :)

like image 68
gbdavid Avatar answered Nov 09 '22 08:11

gbdavid


One person proposes to make the link using the first reponder and expecting everything to hook up at run-time (which looks a bit dodgy and not the way apple would implement it I think...).

I think this first responder method is actually the proper way.

As an example:

Add something similar to the following, in whichever view controller makes sense.

@IBAction func doSomething(_ sender: AnyObject?) {
    print("Do something.")
}

This will magically show up in the first responder:

enter image description here

In your storyboard, right-click the orange "first responder" icon above your window controller, and you should see doSomething in the very long list. You just need to connect that up to your toolbar button.

In the following screen capture, you can see my "Toggle Sidebar" button is connected to the toggleSidebar action in my first responder.

enter image description here

I didn't even have to write this method — it's provided by NSSplitViewController:

    @IBAction open func toggleSidebar(_ sender: Any?)
like image 30
jeff-h Avatar answered Nov 09 '22 09:11

jeff-h