NSWindowController in a storyboard, window subviews and IBOutlets, toolbar actions

So I have a window controller. Its window uses a NSToolbar. The window controller has a toolbar item that should "talk to" an NSTextView.


When you drag out a window controller in a storyboard and remove the "content view controller" you cannot build a view hierarchy in the window controller window itself, which sometimes would be useful. So if I do it from the view controller level...that is drag a text view to a content view controller's view, okay, but now I can't wire an IBAction from the toolbar item in the window controller across to the content view controller.


I could subclass both NSWindowCOntroller and NSViewController, and do some casting...or maybe wire up the action to the first responder. It feels kind of weird though?

>> or maybe wire up the action to the first responder


Bingo! The toolbar is a window-global element, so (in principal) it might apply whatever the first responder is. In your case, only one specific responder would implement the custom action for the toolbar item, so the item would automatically be disabled except when the text field is first responder.


In those terms, I don't think it's weird at all.

But what about a situation where potentially somewhere there is another responder with the same action, maybe even one that the developer at design time does not realize, let's say it's private to some other component he is using. Wouldn't happen in this app, probably wouldn't happen often.


I guess that's the way to do it though with storyboards. I don't know if every toolbar item's action needs to go through the responder chain though. I suppose they have it work this way because the window controller's content view controller could change. So there's that. I've made an app that does this on occasion (not with a toolbar though). It seems more common on mac to have multiple windows though.


Doing things from the view controller level allows us to reuse a controller in more contexts than doing it at the window controller level (I do this even in plain old nibs, by dragging out a view controller and setting the nib name in IB the "old way"), but I still think it'd make sense to allow us to build a window controller in a storyboard that does not rely on a content view controller in certain situations. Just feels a bit limiting. Anyway, I moved this particular window controller to its own nib file.

The name clash is a potential problem with every user-defined action method name, because they're all in the same global namespace. Your best choice is to use an appropriately unique name. (The rules may have changed since Swift brought module-level namespacing, so it's probably only a problem within your app target, not frameworks any more.)


Alternatively, you can put the action method in your window controller, and target the window controller directly from your toolbar item. The window controller would then trampoline the action to the correct target. However, in this case, you would have to handle disabling of the item manually.


>> I still think it'd make sense to allow us to build a window controller in a storyboard that does not rely on a content view controller in certain situations


But you can and always could. Indeed, until fairly recently it wasn't usual to use a view controller at all (because they didn't use to be in the responder chain by default, so it was often easier to put the corresponding code in the window controller, which was). The main problem you're having in this case is that storyboards prevent cross-"scene" connections, and that makes long-established coding patterns harder to implement.


>> I moved this particularly window controller to its own nib file


I happen to think that putting window controllers in nib files is a terrible idea. Window controller should typically be instantiated rather than unarchived. If yours is not instantiated via a storyboard, then you can simply instantiate (alloc/init) it in code. The problem with a nib file is not that it doesn't work — it does — but that you lose control of the exact timing of instantiation, and this can get awkward.

I mean, I designed its window's user interface in its own .xib, rather than in the storyboard.


-(instancetype)initWithData:(NSData*)data
{
  self = [super initWithWindowNibName:NibNameHereForClass];
  if  (self)
   {
       _data = data;
   }
   return self;
}


Technically this is this wc's "designated" initializer, though I don't use the macro because initWithWindowNibName: is not a designated initializer on NSWindowController, and re-implementing nib loading manually is not worth it.


I'm not sure if this is what you though I meant or not, but I don't think there's anything wrong with doing this.


The name clash is a potential problem with every user-defined action method name, because they're all in the same global namespace.


Yeah true. But if you wire up target action directly to a piece, rather than first responder, there's no danger of name clash because the control will only send the message to its target, not a generic responder (which is def. useful in many situations, more often menu items in the application menu bar I think, but toolbar items too for sure, just not always).

Nothing wrong with that approach.

NSWindowController in a storyboard, window subviews and IBOutlets, toolbar actions
 
 
Q