ProperWayToUseNSToolbarInDocumentApps

Crowd-sourced documentation and tutorials by the Cocoa developer community
Jump to: navigation, search


My task today was to learn the basics of NSToolbar and succeed in implementing a toolbar in the document window of a Document-based application. I studied a variety of sources including several open source applications. From that I came up with two different implementations. I am curious if one is/may be preferred and the reason(s) why. Please excuse the verbose details as I want to make sure that even the most inexperienced will be able to use these methods. All of my explanations use the PB and the IB as I haven't moved to XCode.

Granted these are useless toolbars....but they are toolbars nonetheless and more functionality can be added.

Implementation within an NSDocument subclass

1) Open the MyDocument.nib of a new Cocoa Document based application in the IB. Create a new outlet named "myWindow" and connect this outlet to the Window. Save and close.

2) Edit MyDocument.h to be:

  1. import <Cocoa/Cocoa.h>

@interface MyDocument : NSDocument {

   IBOutlet id myWindow;

}

- (void)setupToolbar; - (NSToolbarItem )toolbar:(NSToolbar )toolbar

    itemForItemIdentifier:(NSString )itemIdentifier
willBeInsertedIntoToolbar:(BOOL)flag;

- (NSArray )toolbarAllowedItemIdentifiers:(NSToolbar )toolbar; - (NSArray )toolbarDefaultItemIdentifiers:(NSToolbar )toolbar;

@end

3) Edit MyDocument.m to be:

  1. import "MyDocument.h"

@implementation MyDocument

- (id)init {

   self = [super init];
   if (self) {
   }
   return self;

}

- (NSString )windowNibName {

   return @"MyDocument";

}

- (void)windowControllerDidLoadNib:(NSWindowController ) aController {

   [super windowControllerDidLoadNib:aController];
   [self setupToolbar];

}

- (NSData )dataRepresentationOfType:(NSString )aType {

   return nil;

}

- (BOOL)loadDataRepresentation:(NSData )data ofType:(NSString )aType {

   return YES;

}

- (void)setupToolbar {

   NSToolbar toolbar = [[NSToolbar alloc] initWithIdentifier:@"myToolbar"];
   [toolbar setAllowsUserCustomization: YES];
   [toolbar setAutosavesConfiguration: YES];
   [toolbar setDelegate: self];
   [myWindow setToolbar:[toolbar autorelease]];

}

- (NSToolbarItem )toolbar:(NSToolbar )toolbar itemForItemIdentifier:(NSString )itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag {

   NSToolbarItem item = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier];
   return [item autorelease];

}

- (NSArray )toolbarAllowedItemIdentifiers:(NSToolbar )toolbar {

   return [[[NSArray]] arrayWithObjects:NSToolbarSeparatorItemIdentifier,
                                    NSToolbarSpaceItemIdentifier,
                                    NSToolbarFlexibleSpaceItemIdentifier,
                                    NSToolbarCustomizeToolbarItemIdentifier, nil];

}

- (NSArray )toolbarDefaultItemIdentifiers:(NSToolbar )toolbar {

   return [[[NSArray]] arrayWithObjects:NSToolbarFlexibleSpaceItemIdentifier,
                                    NSToolbarCustomizeToolbarItemIdentifier, nil];

}

@end

4) Build and run.


Implementation within an NSWindowController subclass

1) In a different new project....

2) Create an NSWindowController subclass in the PB, including the header file, called MyWindowController.

3) Edit MyDocument.h to be:

  1. import <Cocoa/Cocoa.h>

@class MyWindowController;


@interface MyDocument : NSDocument {

   MyWindowController			windowController;

}

@end

4) Edit MyDocument.m to be:

  1. import "MyDocument.h"
  2. import "MyWindowController.h"


@implementation MyDocument

- (id)init {

   self = [super init];
   if (self) {
   
   }
   return self;

}

- (void)makeWindowControllers {

   windowController = [[MyWindowController alloc] initWithWindowNibName:@"MyDocument"];
   [self addWindowController:windowController];

}

- (void)windowControllerDidLoadNib:(NSWindowController ) aController {

   [super windowControllerDidLoadNib:aController];

}

- (NSData )dataRepresentationOfType:(NSString )aType {

   return nil;

}

- (BOOL)loadDataRepresentation:(NSData )data ofType:(NSString )aType {

   return YES;

}

@end

5) Edit MyWindowController.h to be:

  1. import <AppKit/AppKit.h>


@interface MyWindowController : NSWindowController {

   NSToolbar                   myToolbar;

}

- (NSToolbarItem )toolbar:(NSToolbar )toolbar

    itemForItemIdentifier:(NSString )itemIdentifier
willBeInsertedIntoToolbar:(BOOL)flag;

- (NSArray )toolbarAllowedItemIdentifiers:(NSToolbar )toolbar; - (NSArray )toolbarDefaultItemIdentifiers:(NSToolbar )toolbar;

@end

6) Edit MyWindowController.m to be:

  1. import "MyWindowController.h"


@implementation MyWindowController

- (id) initWithWindowNibName:(NSString ) windowNibName {

   self = [super initWithWindowNibName:windowNibName];
   myToolbar = [[NSToolbar alloc] initWithIdentifier:@"MyToolbar"];
   [myToolbar setAllowsUserCustomization:YES];
   [myToolbar setAutosavesConfiguration:YES];
   [myToolbar setDelegate:self];
   [[super window] setToolbar:myToolbar];
   return self;

}

- (NSToolbarItem )toolbar:(NSToolbar )toolbar

    itemForItemIdentifier:(NSString )itemIdentifier
willBeInsertedIntoToolbar:(BOOL)flag

{

   NSToolbarItem item = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier];
   return [item autorelease];

}

- (NSArray )toolbarAllowedItemIdentifiers:(NSToolbar )toolbar {

   return [[[NSArray]] arrayWithObjects:NSToolbarSeparatorItemIdentifier,
                                    NSToolbarSpaceItemIdentifier,
                                    NSToolbarFlexibleSpaceItemIdentifier,
                                    NSToolbarCustomizeToolbarItemIdentifier, nil];

}

- (NSArray )toolbarDefaultItemIdentifiers:(NSToolbar )toolbar {

   return [[[NSArray]] arrayWithObjects:NSToolbarFlexibleSpaceItemIdentifier,
                                    NSToolbarCustomizeToolbarItemIdentifier, nil];

}

- (void) dealloc {

   [myToolbar release];
   [[self window] setDelegate:nil];
   [super dealloc];

}

@end

7) Build and run.

Any comments/suggestions/opinions?

-NedO

Just wondering why you used the window controller as the toolbar delegate. This seems to violate the principle of giving each class a single purpose.

I don't see why not. If the class's single purpose is "manage the window", then "manage the toolbar" would fall under that.


...or consider making it a category on the window controller...

No, because then all windows would use the same toolbar...

only windows which are controlled by a MyWindowController

Oops, I thought you meant a category on NSWindowController. My bad.

Personal tools
Namespaces

Variants
Actions
Navigation
Toolbox