When a Cocoa application starts up, all of the objects stored in its main nib are loaded into memory and initialized. This takes time (the more objects, the more time), and until the nib is loaded, your application can’t do anything else. This can be real drag, especially if your program doesn’t need most of the objects in the main nib for normal operation. For this reason, Cocoa lets you take objects that you don’t use often and place them in separate nibs. You can arrange for these auxiliary nibs to be loaded only when they are needed.
Auxiliary nibs should be used for most panels that do not need to be displayed when your program first starts up. Instead, the files are loaded the first time that the panel is needed. Once the panel is loaded, it is resident in your computer’s memory — additional attempts to make the panel display go much faster.
Now we’ll arrange for our Calculator application to use a separate nib for its About box. This will consist of three steps:
Creating the new nib that contains the About box
Modifying the Controller class to load this new nib (and thereby display the About box)
Modifying the Calculator’s
MainMenu.nib
accordingly
For pedagogical reasons, we’ll perform these steps in the order (ii), then (iii), and finally (i).
To start, we’ll modify the Controller object to add two things:
You might think that the easiest way to create the new outlet and
action method is to add them in IB’s inspector
window, as we did in the previous chapter. You can do this. However,
because we have added code to the Controller.h
and Controller.m
class files, if you add the
outlet and action methods in IB, you will need to use
Apple’s File Merge utility to merge the changes that
you made in these files with the changes that IB makes. This can be
somewhat complicated, and if you make a mistake, you will lose all of
the specialized coding that you have added so far!
Instead, we believe that the easiest way to add new
outlets and action methods to the
Controller class is to use a text editor to add them directly to the
Controller.h
and
Controller.m
class files, and then use
IB’s Classes → Read Files command
to read them into IB’s internal description of the
class. The new outlet and action will then appear in
IB’s Controller class inspector, and
we’ll be able to use them to make connections with
user interface objects. (Recall that we parsed an outlet from the
Controller.h
file into IB in the previous
chapter; here we’ll parse a method in a similar
fashion.)
Back in PB, insert the two lines shown here in bold into
Controller.h
, and save the file:
@interface Controller : NSObject
{ IBOutlet id readout; BOOL enterFlag; BOOL yFlag; int operation; double X; double Y; IBOutlet id aboutPanel; } - (IBAction)clear:(id)sender; - (IBAction)clearAll:(id)sender; - (IBAction)enterDigit:(id)sender; - (IBAction)enterOp:(id)sender; - (void)displayX; - (IBAction)doUnaryMinus:(id)sender;- (IBAction)showAboutPanel:(id)sender; @end
The aboutPanel
outlet will eventually be set to
the id of our new About box object. The showAboutPanel: action method will be coded to
display the About box in response to the user’s
choosing the Calculator → About Calculator menu
command.
Make sure you save the edited Controller.h
file
at this point, because IB will read the class interface file from
disk, not from your edited but unsaved copy.
Now double-click the MainMenu.nib
resource in PB
to open the interface in IB. (Following that, you again might want to
choose IB’s Hide Others menu item to simplify your
screen).
Select the Controller class (under NSObject) under the Classes tab in
IB’s MainMenu.nib
window and
choose IB’s Classes → Read Files
menu item.
Double-click Controller.h
in the Read Files
panel that opens to parse the new definition of the Controller class
from the updated Controller.h
file on disk.
Type Command-1 to display IB’s Attributes Info
dialog; you should see the new aboutPanel
outlet
and the new showAboutPanel: action
method, as shown in Figure 6-5. (If you
don’t see them, you probably didn’t
save the Controller.h
file after editing).
Back in PB, insert the entire new showAboutPanel: method, shown here in
bold, into Controller.m
. We
suggest that you place it just before the @end
directive.
- (IBAction)showAboutPanel:(id)sender { if (aboutPanel == nil) { if (![NSBundle loadNibNamed:@"AboutPanel.nib" owner:self] ) { NSLog(@"Load of AboutPanel.nib failed"); return; } } [aboutPanel makeKeyAndOrderFront: nil]; }
Every Objective-C instance variable is initialized to nil (0) when an
object is created.[13] When the Calculator application
starts up, the aboutPanel
outlet in the Controller
instance will not be explicitly set, so its value will be nil. Thus,
when the showAboutPanel: method is
invoked the first time, the conditional if
statement will cause the loadNibNamed:owner:
message to be sent to the
NSBundle
class.
A bundle in Cocoa is a collection of files in a folder that is used to store dynamically loaded code, icons, sounds, objects, and/or other kinds of information. A nib is a special kind of bundle that is used by IB to store user interfaces. Applications are another special kind of bundle that are created by PB and used by the Finder to put together all of the files that go into an application. Frameworks and IB palettes are additional examples of bundles.
The call to the loadNibNamed:owner:
method loads the nib bundle containing the About box (which
we’ll create later in this chapter) from the nib
AboutPanel.nib
. When the nib is loaded, it
automatically initializes the aboutPanel
outlet of
its owner to the id of the About box. (We’ll show
you how to create the About box and set up this initialization a bit
later.) Finally, the showAboutPanel:
method will send the About box object the makeKeyAndOrderFront: message, which makes the
About box the key window and brings it to the front of the window
display list (making it visible). If something goes wrong with the
loading, the NSLog( )
[14] function will display an error message in the system
console and in PB’s Run pane if the application is
run from PB. This can be very useful in debugging.
The second time the showAboutPanel:
method is called, the aboutPanel
outlet will
already be initialized. Thus, the statement sending the loadNibNamed:owner: message will be skipped,
preventing a second copy of the nib from being loaded. Because the
nib that we loaded the first time through is still in memory, the
About box will be displayed without the loading delay.
Apple’s “Aqua Interface Guidelines” say that bringing up a panel should be a safe and reversible option. A user should be able to make the panel disappear by clicking a cancel or close button without any ill effects for the application. Our About box will meet this requirement.
Next we’ll modify the About Calculator menu item command in the Calculator’s main menu so that it invokes our showAboutPanel: method.
As we saw earlier in this chapter, the Cocoa development environment gives every new Cocoa application a built-in About box. This panel is displayed by the NSApplication object when it receives the orderFrontStandardAboutPanel: message. To have our application display our custom About box (which we haven’t created yet), we’ll need to change the About Calculator menu item so that it invokes our method, instead of the default method.
To do this, follow these steps:
Back in IB, click Calculator in the Calculator main menu to open the submenu.
Select the About Calculator menu item.
Type Command-2 to display the Connections Info dialog for the NSMenuItem, as shown earlier in Figure 6-2.
Make sure that the orderFrontStandardAboutPanel: action method is highlighted in the Connections inspector and then click the Disconnect button to remove the default connection. The dimple next to the method should disappear.
Control-drag from the About Calculator menu item to the Controller
object in the MainMenu.nib
window to create a
new connection, as shown in Figure 6-6.
Select the showAboutPanel: action in the NSMenuItem Info dialog (see Figure 6-6), and then click the Connect button at the bottom of the dialog. The Connect button will become a Disconnect button, as shown in Figure 6-6. (You can also double-click the action method.)
We have now arranged for the About Calculator menu command to invoke the Controller’s showAboutPanel: action method.
There is one other modification that we should make to the Calculator menu, concerning the menu command that brings up a Preferences panel. Because our Calculator application doesn’t have a Preferences panel, we should remove this menu item.
Still in IB, select the Calculators → Preferences menu item by clicking it once.
Type Command-X to cut the menu item from the menu. Your menu should now look like the one in Figure 6-7.
To complete our About box addition, we will create a separate nib for the About box. This nib will be loaded by the loadNibNamed:owner: message that the Controller’s showAboutPanel: method sends to the NSApplication object.
Choose IB’s File → New command. IB’s Starting Point panel will appear.
Select Cocoa → Empty, as shown in Figure 6-8, and click New.
Note that there are now two Nib File windows at the lower left of the
screen, one for MainMenu.nib
and the other
(“Untitled”) for the new nib that
we created.
Type Command-S to save this new nib, and you’ll see a “Save as” sheet slide out from under the title bar.
If necessary, click the down arrow button to reveal the full “Save as” sheet. Find the folder Calculator/English.lproj
(the English-language project directory of the Calculator source code), shown in Figure 6-9.
Save the new nib as AboutPanel.nib
. Before
saving, IB will ask you if you want to add the new nib to the
Calculator project, as shown in Figure 6-10. Click
Add.
After you add the new nib, note that the name in the title bar of the
second Nib File window changes to
AboutPanel.nib
, as shown in Figure 6-11. Note also that there are only two instance
objects, File’s Owner and First Responder, in the
new nib (compare with MainMenu.nib
in Figure 6-11). Because IB knows this is an empty auxiliary
nib, it doesn’t automatically provide you with
MainMenu and Window instances, as it did for the main nib. Clearly,
another MainMenu object is not needed. Also, if you activate PB,
you’ll notice that
AboutPanel.nib
has been added to the Calculator
project as a resource, alongside MainMenu.nib
and InfoPlist.strings
. Thus, when the Calculator
application is built, AboutPanel.nib
will
automatically be copied into the Calculator.app
application bundle.
If PB is active, double-click AboutPanel.nib
in
PB’s main window to reactivate
AboutPanel.nib
in IB. (If you double-click any
file in PB’s Groups & Files pane, PB will open
that file in the appropriate application.) This step may not be
necessary.
Back in IB, make sure the AboutPanel.nib
window
is active (or key). To simplify the screen, we
recommend that you minimize the MainMenu.nib
window and hide the other applications.
Click the Cocoa-Windows button (which should be the fourth button from the left) in IB’s Palettes window toolbar. See Figure 6-12.
Drag the icon with the Panel label from the Cocoa-Windows palette and
drop it in the AboutPanel.nib
window (you can
also drop it on the desktop). This will create an empty panel with
the title “Panel” and will add the
panel to the AboutPanel.nib
nib. See Figure 6-13.
Click anywhere inside the new panel to select it, then type Command-1 to bring up the NSWindow Attributes Info dialog (NSPanel is a subclass of NSWindow).
Change the title of the panel to “About the Calculator” in the Info dialog.
Click the Cocoa-Views button (which should be the second one from the left) at the top of IB’s Palettes window.
Customize the panel’s text by dragging and dropping text icons (e.g., System Font Text) from the Cocoa-Views palette into the panel.[15] Type Command-T to bring up the Font panel to change the size, etc. of the type. See what we did in Figure 6-14. (Your About box won’t look exactly the same as our screen shot yet, but it doesn’t matter.)
Now drag the horizontal-line icon from below the radio buttons in the Cocoa-Views palette and drop it in the new panel. Resize it and change its location to be a separator, as shown in Figure 6-14. Also, resize the panel itself as appropriate.
Note that we’ve left some space at the upper right of the “About the Calculator” panel. We’ll use that space to place our application icon before the end of the chapter.
Recall that we want the Controller instance to load
AboutPanel.nib
when the user chooses our
Calculator’s Calculator → About
Calculator menu command. We’ve already set up the
target/action connection from the menu command to the Controller, but
we have not yet set up any communication between the Controller and
the About box. The About box is part of the separate
AboutPanel.nib
, and this nib
doesn’t even
“know” that a Controller class
exists. You can see this by looking at the subclasses of NSObject
under the AboutPanel.nib
Classes
tab — there’s no Controller class.
We’ll set up the required linkage between the new
nib and the Controller in the next few steps.
Select the NSObject class in the Classes pane in the
AboutPanel.nib
window.
Note that the Controller class does not appear (recall that it did
show up in the MainMenu.nib
window). To change
this, we’ll make IB read the
Controller.h
information into
AboutPanel.nib
.
Choose the Classes → Read Files menu command, and the Read Files dialog will open.
Browse the filesystem to select the Controller.h
file (under ~/Calculator
) in the Read Files
panel, then click the Parse button.
The Controller class should now show up in the
AboutPanel.nib
window, as shown in Figure 6-15. Now AboutPanel.nib
knows
about the Controller class outlets and action methods, and it also
knows that it is a subclass of NSObject. Of course, all of this
information is contained in Controller.h
.
Alternatively, you could have informed
AboutPanel.nib
about the Controller class by
simply dragging the Controller.h
file icon from
the Finder or PB and dropping it into the
AboutPanel.nib
window. When you do this, IB will
automatically parse the Controller class definition on disk and
insert the Controller class into the
AboutPanel.nib
class hierarchy.
We’ll use this quicker technique in subsequent
chapters.
We still need to make a connection from the Controller instance
(created by MainMenu.nib
) to the About box
object (created by AboutPanel.nib
) in order to
initialize the Controller’s
aboutPanel
outlet. We cannot do this using a
different Controller instance instantiated by
AboutPanel.nib
— we must use the instance
instantiated by MainMenu.nib
, because
that’s the one that controls the running Calculator
application! To do this, we will use the File’s
Owner icon in AboutPanel.nib
. The
File’s Owner is an object that
“owns” a nib.[16] It’s the argument that is passed
to the NSBundle class when the nib file is loaded.
We’ve already arranged for this argument to be the
id of the Controller object that is running the Calculator
application, so all we need to do is to make sure that
AboutPanel.nib
sets the outlet when it is
loaded.
Click the Instances tab in the AboutPanel.nib
(not MainMenu.nib
) window to see the three
objects for this nib.
Inform IB that the File’s Owner in
AboutPanel.nib
will be of the Controller class.
Do this by clicking the File’s Owner icon in the
AboutPanel.nib
window and then selecting
Controller in the File’s Owner Info dialog, as shown
in Figure 6-16. (If the Info dialog
isn’t visible, type Command-1).
Make the aboutPanel
outlet in the
File’s Owner point to the About box. Do this by
first Control-dragging from the File’s Owner icon in
the AboutPanel.nib
(not
MainMenu.nib
) window to the (About) Panel icon
in the same window. Finish the job by double-clicking the
aboutPanel
outlet in the File’s
Owner Info dialog. See Figure 6-17.
When AboutPanel.nib
is loaded, it will create
the About box. The last step we completed arranged for the
aboutPanel
outlet in the File’s
Owner object (i.e., the Controller object that loads
AboutPanel.nib
) to be set to the id of this
newly created About box (represented by the Panel icon in
AboutPanel.nib
).
You might still be wondering about the File’s Owner.
Recall the following Objective-C statement in the
Controller’s showAboutPanel: method; when the program runs,
this statement loads AboutPanel.nib
:
[NSBundle loadNibNamed:@"AboutPanel.nib" owner:self]
The File’s Owner is the object that is specified by
the self
in the clause owner:self
. In this case, the owner is the
Controller instance (self ) that
sends the above message to NSBundle. Thus, the
aboutPanel
outlet in the Controller instance is
set to the id of the About box that is loaded.
The File’s Owner icon is called a proxy
object
because it is not a real object;
instead, it is a proxy for a real object that was instantiated when
another nib was loaded (in this case, that nib is
MainMenu.nib
). Setting File’s
Owner outlets and sending messages to a File’s Owner
object are the easiest ways to communicate between nibs.
Now, let’s run the Calculator application and see how it works:
Activate PB and click the build and run button for the Calculator target. Save all files before building.
With Calculator running, choose Calculator → About Calculator.
The first time you choose the Calculator’s
Calculator → About Calculator menu command, you may
notice a slight delay before the About box appears (and you may hear
your hard drive reading the nib file). This delay is the time that it
takes to load the nib file AboutPanel.nib
into
memory. However, if you close the About box and then choose
Calculator → About Calculator again, the About box
should appear immediately because it’s being read
from memory, not from disk.
[13] Note that Objective-C initializes only instance variables. Variables that are static or local to a method are still uninitialized, just as in standard ANSI C.
[14]
NSLog( )
works a lot like
printf
, except that the first argument is an
NSString and the result is sent to the system console.
[15] It’s possible that the names of the text icons will change in later releases. Palettes may change too.
[16] The File’s Owner icon is actually a proxy for the object id that is passed to the method that loads the nib into memory.
3.145.178.151