15

I've been trying to build on a Cocoa app that uses Swift and Storyboard in Xcode 6, but how can I use NSToolbar there?

In Xcode 5 and xib, you can add NSToolbar from within Object Library to any .xib files and then click on the added toolbar to expand it and drag and drop a connection from any items there to a AppDelegate.h file. In this way you can create a IBAction or IBOutlet connection. I confirmed that this can also be done if you use Swift and non-storyboard in Xcode 6. However, it looks like this is not the case in Xcode 6 and Storyboard environment.

I first created a project that uses Storyboard in Xcode 6, but then, I wasn't able to add a NSToolbar from within Object Library to a View Controller in Storyboard. So I added it to Window Controller's Window object in Storyboard. However, in this way I cannot create those connections from any items in the expanded toolbar to either AppDelegate.swift or ViewController.swift.

So my question is:

  • Is it feasible to create a storyboard app that uses NSToolbar?
  • If it is feasible, is the addition of NSToolbar to the Window Controller the proper way to use NSToolBar in Storyboard?
  • Finally, how can I create @IBOutlet and @IBAction connections there?

UPDATE

I found that the accepted answer by @GeorgeVillasboas only works for @IBAction. I am still looking for how to create an @IBOutlet connection...

Blaszard
  • 30,954
  • 51
  • 153
  • 233

6 Answers6

28

I had this very same problem. The solution works for both Objective-C and Swift projects.

Working with Storyboards on OSX, it creates an instance of NSWindow and segues to another NSViewController as its Window Content Segue, as you described.

On your ViewController, create a standard IBAction to receive the action when the toolbar is clicked. To wire it up with the NSToolbar, just control-drag (or leftClick-drag) from your NSToolbarItem to the FirstResponder object, as shown on the picture below.

Wiring up a NSToolbar on XCode 6

This will open a HUGE list of available connections. Your IBAction will be on that list. Just selected it and you're good to go.

Hope this helps!

  • I made an `@IBAction` connection between the toolbar item and first responder, but the method is still not emitted... – Blaszard Jul 18 '14 at 01:22
  • 2
    @Gardecolo did you managed to put your ViewController as your NSWindow's delegate? Over here I compiled a simple and working example of it in Swift. You should be able to get it working with it. Hope it helps. Best. G. - https://github.com/ghvillasboas/SimpleNSToolbarWire – George Villasboas Jul 18 '14 at 13:35
  • 1
    You were right, that the issue was from the lack of the adoption of `NSWindowDelegate` and `window.delegate = self`. This worked in custom Toolbar Item, but didn't on Colors or Fonts Toolbar Item, FYI. But I don't need it, so no problem. Thank you for your courtesy. – Blaszard Jul 18 '14 at 13:53
  • 5
    @GeorgeVillasboas what about IBOutlets? Couldn't find a way to connect. – modusCell Sep 23 '14 at 23:52
  • 6
    Apple needs to make a barbecue and invite Cocoa and Cocoa Touch teams, so they can finally meet and know each other and start creating one unified set of controls and APIs for both OSX and iOS! And by the way Cocoa is far more complex and problematic compared with iOS. It is time for the Cocoa team to copy APIs from Cocoa Touch. Apple, I am watching you! – Duck Dec 18 '14 at 19:30
  • @GeorgeVillasboas Hello sir, i am new to swift from Java. I did all the procedures unto "NSToolbarItem to the FirstResponder object" but i don't understand "did you managed to put your ViewController as your NSWindow's delegate?". Can u please explain it for me. I hope that is why mine is not working – Abin Dec 31 '14 at 13:17
  • 2
    @Abin check out this sample project. Hope it helps. https://github.com/ghvillasboas/SimpleNSToolbarWire – George Villasboas Jan 01 '15 at 20:26
  • 1
    @GeorgeVillasboas No words to explain my thanks. Thank you so much – Abin Jan 02 '15 at 05:36
  • This worked for me. Also I was able to connect to a IBOutlet by first manually creating the IBOutlet code (e.g @property (weak) IBOutlet NSSearchField *searchLocation;) and then putting XCode into Assistant editor mode showing toolbar item on left and code on right, then rh mouse click on the control to bring up its context menu, and then click/drag from Referencing Outlets to the IBOutlet code. – gpdawson Mar 25 '15 at 03:09
  • Been working with OS X storyboards for a month now. Just wanted to add that it seems that the NSWindowController added by default to the storyboard is just next in the responder chain after its content view controller and thus the IBActions added in the view controller also appear in the window controller's first responder actions. It also took me quite a bit to understand this. – nstein Jun 05 '15 at 09:50
11

Here's an answer that doesn't rely on run-time hook-ups - @cdalvaro's answer gets most of the way there for some applications, but isn't full, and it requires the ViewController to know about the artificial NSWindowController, which doesn't feel right.

Like @cdalvaro, the first step is to build your own subclass of NSWindowController, and to set the Storyboard WC to that class. You can then create all of your connections to and from the NSToolbar (both @IBOutlets & @IBActions) in the new WindowController. So far so good.

The last step, which I haven't seen anywhere else, is how to refer to the ViewController in the WindowController - you can't create an @IBOutlet to it - for the same reasons that we got here in the first place - you can't create references across scenes in the Storyboard. However, the WindowController must have a reference to the ViewController, and it does... self.window!.contentViewController! as! ViewController

Here's a complete WindowController with a checkbox that sets values in the ViewController, without the ViewController having to know anything...

class MyWindowController: NSWindowController {

    var viewController: ViewController {
        get {
            return self.window!.contentViewController! as! ViewController
        }
    }

    @IBOutlet weak var aSwitch: NSButton!
    @IBAction func toolbarActionA(sender: AnyObject) {
        println("toolbarActionA")
        self.viewController.a = !self.viewController.a
        self.aSwitch.state = self.viewController.a ? NSOnState : NSOffState
    }
}
Grimxn
  • 22,115
  • 10
  • 72
  • 85
4

This helped me for the IBOutlet solution you are looking for: https://stackoverflow.com/a/27555237/3398062

Update (explanation)

I discovered this thread because I was trying to create a Circular Progress Indicator inside the toolbar and I didn't know how to animate it from the ViewController since the IBOutlet was declared inside a custom WindowController.

Finally, I found the post that I have added above which describes how to access to IBOutlets from other classes.

In essence what I have done is the following:

  • Create a custom NSWindowController subclass (CustomWindowController) for the Window Controller so I can add the IBOutlet for the ProgressIndicator:

Code Example:

@interface CustomWindowController : NSWindowController

@property (weak) IBOutlet NSProgressIndicator *progressIndicator;

@end
  • Then in the ViewController class, in the method I want to use to update the state of the Progress Indicator, I create and object of the custom Window Controller.

Code Example:

CustomWindowController *customWindowController = self.view.window.windowController;`
  • Finally, to change the state of the Progress Indicator there is only to call the method from the created custom object.

Code Example:

[customWindowController.progressIndicator startAnimation:sender];

or

[customWindowController.progressIndicator stopAnimation:sender];
Community
  • 1
  • 1
Carlos D. Álvaro
  • 561
  • 1
  • 6
  • 12
2

This video helped me how to create a toolbar without writing a single line of code: https://www.youtube.com/watch?v=XSQocHG3IjA

You can add the 'Window Controller' item from the Object Library (if you don't have one), connect to a View Controller (where you want your toolbar to display) and follow the video! For custom Toolbar buttons add 'Image Button' item to the Toolbar just by dragging from the Object Library. Then you can pick an image for a button, set the size and so on...

balazs630
  • 3,421
  • 29
  • 46
0

Here is a general solution for the outlets and actions. it allows you to preform all the the same functions as an iboutlet would for a tool bar item and also allows you to set the button to a function instead of creating an ibaction. hope it helps :P

override func viewDidLayout() {
var x = self.view.window?.toolbar?.items[1].label
println(x)
if(self.view.window?.toolbar?.items[0].label! != "Check")
{
    toobarediting()
}
println("didlay")
}

func toobarediting() {
self.view.window?.toolbar?.insertItemWithItemIdentifier("Check", atIndex: 0)
}

func toolbarcheck(functiontoset: Selector) {
var y = self.view.window?.toolbar?.items[0] as NSToolbarItem
y.action = functiontoset
if(functiontoset != nil)
{
    y.enabled = true
}
}

Here is a link to my question attempting to get a cleaner answer http://www.stackoverflow.com/questions/27371439/use-nstoolbar-outlet-xcode-6-and-storyboard/27374449 but it looks like from the answers i have gotten so far this is the best option.

nsij22
  • 869
  • 10
  • 20
-1

The same problem of IBOutlets also applies to KVO. One of the features of the NSSplitViewController is the canCollapse property for each split view item which supports KVO, but this is unusable in IB just like IBOutlets.

The only workaround I can see is to add a NSObjectController to each scene and when the scene loads, set the object controller's object to the object in the other scene (already loaded) that you want to reference.

Mark Lilback
  • 1,154
  • 9
  • 21