NEXTSTEP Interface Builder Q&A

Creation Date: August 14, 1998
Keywords: NeXTSTEP

Disclaimer

This document pertains to the NeXTSTEP operating system, which is no longer a supported product of Apple Computer.  This information is provided only as a convenience to our customers who have not yet upgraded their systems, and may not apply to OPENSTEP, WebObjects, or any other product of Apple Enterprise Software.  Some questions in this Q&A document may not apply to version 3.3 or to any given specific version of NeXTSTEP.

Q:  How do I specify an icon for my application?  Or for the types of files it uses?  How do I get my application to be launched by the Workspace Manager?

For 2.0:
A:  You'll need to create the appropriate TIFF files and then add them as Project Attributes in InterfaceBuilder.  Here are the steps:

(1)  Make TIFF files for your icons.  You might want two pictures: one for the application itself, and another for files "belonging" to that application.  (For example, WriteNow files ending in ".wn" have their own icon.) Icons may be any depth and the TIFF file can contain multiple images, allowing it to store the look of the icon for different frame buffers.  It's recommended that you use a 48x48 grayscale image, 2 bits per channel, with alpha, and, if your application makes use of color, a 48x48 RGB image at 4 bits per channel, with alpha.  Combine the two images with tiffutil, the command line program.

(2)  Use the Inspector panel in InterfaceBuilder to modify the Project Attributes of your application.  Use the Set button to select the icon you want.  Be sure to add the extension for your documents, without the period.  (If you do include the period, InterfaceBuilder will accept it and compile without complaint, but the extension won't work properlyÐyour documents won't display the specified icon.)   Be sure to save your project, then recompile.

(3)  You do not need to put the executable in your path for Workspace to display the application icon; you only need do this if the application opens documents that you'd like to be able to open by double-clicking.

For 3.0:
A:  You'll need to create the TIFF files and then add them as Attributes in Project Builder.  Here are the steps:

(1)  Make TIFF files for your icons.  You might want two pictures: one for the application itself, and another for files "belonging" to that application.  (For example, WriteNow files ending in ".wn" have their own icon.)  

(2)  Select the Attributes button in the project window. The Application Icon well displays the application icon.  The default application is used if you don't provide one of your own.  To associate a new icon with the application, drag its TIFF file from the Workspace into the well.  The file is copied to the project directory, although it doesn't appear in any of the categories shown in the Files display.  The Document Icons and Extensions well is where you indicate what types of files your application can open.  Drag the TIFF file containing the icon into the well.  Once the icon is in the well, change its label to match the file  extension. Be sure to save your project, then recompile.

(3)  You do not need to put the executable in your path for Workspace to display the application icon; you only need to do this if the application opens documents which you'd like to be able to open by double-clicking.

Q:  I've made connections in InterfaceBuilder like I have a zillion times before.  So, why are all of my outlets nil when I run my program?

A:  In earlier releases, you had to provide a method to set each outlet that you declared in a new class.  For example, if your subclass had an outlet named myOutlet, the source code for your subclass had to declare and implement this method:

	- setMyOutlet:anObject
	{
		myOutlet = anObject;
	 	return self;
	}
	
In InterfaceBuilder, the Unparse command in the Classes window's pop-up list would create these "set" methods automatically.  

These outlet initialization methods are no longer required.  However, for backward compatibility, if an object responds to such a message, the runtime system uses them to initialize outlets.  Basically, the runtime system does this:
	
	if ([yourObject respondsTo:@selector(setMyOutlet:)])
		setMyOutlet:anObject

All is hunky dory, if you remember the days when these methods were required.  Now that InterfaceBuilder does not generate them automatically, it is not obvious that these method names have special meaning.  So, if you have a method name setMyOutlet you must set the outlet within that method.  If you don't initialize the outlet, then myOutlet is nil at runtime.

Overall it is best to avoid setMyOutlet style method names altogether, unless you are consciously taking advantage of this "feature," and are doing additional initialization within the "set" method.  Care must be taken, as the order in which outlets and other objects are  initialized is neither guaranteed nor predictable.

Q:  How do I create hierarchical views in InterfaceBuilder?

A:  The only ways that InterfaceBuilder provides are:

1)	You can use the Group command while a number of views are selected.  This creates a Box object that encloses these views, becoming their superview.  If you don't want a title or a border use the Inspect command, set the No Title and No Border options, and then set the horizontal and vertical offsets to 0. 

2)	Starting with Release 2, there is a Group in ScrollView command. By choosing this command, you can put any selected View in your application's window into a ScrollView.  See the 2.0 or 3.0  InterfaceBuilder release notes.

Note: For NeXTSTEP Release 3, you should use the Inspector command from the Tools menu to open the Inspector panel, then follow the steps described in (1) to set the grouping attributes.

Q:  How do I create a nib module that is owned by something other than a subclass of application or a subclass of object?

A: When you create a new module, InterfaceBuilder prompts you to choose what is going to own it:  a subclass of object or a subclass of application.  If you had planned for the owner to be one of those two, that works well.  But what if you want the nib module to be owned by a subclass of a subclass of Object or by a subclass of Control or any other class in your class hierarchy?  When you first create the new nib module, go ahead and indicate to InterfaceBuilder that you want it to be owned by some subclass of Object.  We will change it later.  Add the class you want as the owner to your class hierarchy.  Select the File's Owner icon in the nib window of InterfaceBuilder (the small window with the icons in the lower left corner of screen).  Choose Attributes from the Inspector panel pop-up list.  There is a scrolling list of custom classesÐselect your custom class from the list and click "OK."  You've done it!

Note that in NEXTSTEP Release 3 your custom class is automatically selected once you click a selection from the list. The "OK" button has been removed. 

Q:  I have created a custom palette.  I want to do some additional initialization in my palette, once the user has dragged it into one of the application's windows, or just before they run the InterfaceBuilder Test Interface mode. Which method does InterfaceBuilder call to communicate with my palette?
  
A:  When you drag an object off the Palette, a read: and an awake: message are sent after the first write: message.  Subsequent dragging of the same object generates only a read: and an awake: message. However, each time you go into the InterfaceBuilder Test Interface mode, new instances of each palette object are created so each of those real instances are sent a write: message, then a read: and an awake: message.

This is because InterfaceBuilder treats objects differently when they are in "build" mode vs. "test" mode.
Note also that during the archiving process, write: methods may be performed twice, so they shouldn't do anything other than write instance variables to a typed stream.

Q:  In the process of designing an interface, I placed two buttons and one box in my window.  I sent the box to the back of the window and then resized it so it would be under the buttons.  However, the three objects move independently and I want them to move together!

A:  The geometrical superposition of a Box and other objects in the window editor does not imply the creation of a view hierarchy even if it looks that way.  To create an actual hierarchy, delete the existing box, select the other objects, and choose "Group" from the Layout menu.  This creates a new Box as the superview of those objects.  Once you have created a view hierarchy, the objects move together as a unit. 

You can select one of the objects in the box by double-clicking it.  Notice that the Box editor is opened (there is a dark gray border around the Box) and you can move the objects around inside, but not out of, the boundaries of the box.

Q:  I've created a loadable palette for InterfaceBuilder according to the instructions in the Release Notes.  I then created an inspector, and wrote all of the appropriate routines.  Now, I'm trying to use this from within InterfaceBuilder.  If I drag my object from the palette, and then modify some of its attributes and select OK, the inspector doesn't remember the settings when I select the object again at a later time.  Why?

A:  This isn't documented, but within the ok:sender method for your inspector object, you should call the ok: method for its super class, and return that value. Here's a template for your ok: method.

	ok:sender
	{
		/* do a bunch of stuff in here */
		return [super ok:sender];
	}

This is also true for the revert: method.  You should call the super class's revert: method from within the object's.  Like this:

	revert:sender
	{
		/* do a bunch of stuff in here */
		return [super revert:sender];
	}

Q:  I've created a palette object with subviews.  These subviews are created within the init: method for my object.  I store the id's for these subviews in instance variables in the object.  When I instantiate one of these objects by dragging it from the palette, the subviews are thereÐthey are visibleÐyet none of my code for resizing or accessing these subviews is working.  Why?

Q:  When I instantiate and initialize one of my palette objects, the instance variables do not contain the values to which they were initialized.  What's going on?

A:  If you debug the object that you have written within the context of InterfaceBuilder (try using fprintf(stderr, ...) statements, if nothing else), you will discover that all of the instance variables and outlets are nil, even though at one point all of the instance variables were initialized and the objects to which the outlets once pointed still exist.  This is because the object is being archived and unarchived.  It is your responsibility to provide the specific archiving and unarchiving code for each of your objects.  This is accomplished within the read: and write: methods for your object.  Within those methods, you must read and write all of the instance variables for your objectÐeven the id's.  If you do not do this, then instance variables contain bogus values, and outlets become disconnected even though the objects themselves exist.  (View archiving automatically handles archiving and unarchiving subviews.)

Here is a code snippet which illustrates what you must do.  The object in question contains six instance variables: 1 string, 2 booleans, and 3 id's which point to subviews of the object.

	- read:(NXTypedStream*)stream
	{
		[super read:stream];
		NXReadTypes(stream,"cii", myTitle, &bordered, &gridEnabled);
		
	/* read in the id's for the outlets */
		myTitleField = NXReadObject(stream);
		grapherView = NXReadObject(stream);
		nameField = NXReadObject(stream);
		return self;
	}

	- write:(NXTypedStream*)stream
	{
	    [super write:stream];
		NXWriteTypes(stream,"cii", myTitle, &bordered, &gridEnabled);

	/* write the id's for the outlets */
		NXWriteObjectReference(stream, myTitleField);
		NXWriteObjectReference(stream, grapherView);
		NXWriteObjectReference(stream, nameField);
		return self;
	}

Q:  I have designed a custom palette in InterfaceBuilder which contains a subclass of View called MyView.  When I create a new application, load the palette and test it in Test Interface mode, everything looks fine.  When run make on the application there are no errors.  However, when I launch the application from the Workspace Manager it dies because the MyView object is not in the run module.  What am I doing wrong? 
	
A:  When you create a loadable palette in InterfaceBuilder with a special class like MyView, you need to also distribute the code (in some form) for that class.  This then needs to be linked into the application.  There are two ways to do this:

1)	Distribute the .m and .h for the class.
	In NEXTSTEP Release 2, you would then add these to the "[.hm] (class)" category of the project.
	
	In NEXTSTEP Release 3, you opens his project folder PB.project via ProjectBuilder, and adds the [.m] file by selecting Classes in the browser and choosing Add from the Files Menu. The corresponding header file is then added automatically, and can be viewed under the Headers folder in the browser. 
    
2)	Distribute the .o and .h for the class.
	In NEXTSTEP Release 2, you would then add the .h to the ".h (other)" category of the project.  You would also add the .o to the "Other files" category of the project.
	
	In NEXTSTEP Release 3, you would add the .h by selecting Headers  in the Project Builder browser and choosing Add from the Files Menu. An pane opens, and the you select the header file to be added.

In both NEXTSTEP versions, you also need to create a Makefile.preamble which contains the following line:
    
	OTHER_OFILES = MyView.o

See /NextLibrary/Documentation/NextDev/DevTools/01_PuttingTogether.rtf for more information.

See also:  The InterfaceBuilder 2.0 Release Notes for more information on building and using custom palettes for NEXTSTEP Release 2, and the InterfaceBuilder 3.0 Release Notes for NEXTSTEP Release 3.
	     
See also the examples in /NextDeveloper/Examples/InterfaceBuilder.  Note that BlinkPalette and SketchPalette only exist in NEXTSTEP Release 2.

Q:  I am having trouble removing an image inserted by reference that I'd added to my nib file.  I have no trouble deleting an image inserted when I copying it into the nib file.

A:  When you look at the Image suitcase within InterfaceBuilder you can visually determine how an image was added by examining the title.  If the title is black, the image was copied into the nib.  If the title is grey, the image was added by reference.  In order to delete a "copied" image you can select it and delete it either by pressing the Delete/Backspace key or by choosing Delete from the Edit menu.  In order to delete a "referenced" image you must remove it via ProjectBuilder even if it was added in InterfaceBuilder.

To delete an image in ProjectBuilder select the Files browser, select Images, select the image you wish to remove and delete by choosing Remove form the Files menu.

Q:  How can I specify one of the system bitmaps as the icon for my button in InterfaceBuilder?  The Icons suitcase only includes a handful of all the system bitmaps shown in /NextLibrary/Documentation/NextDev/Summaries/07_SysBitmaps/SysBitmaps.rtfd
(or page 7-2 of the hard copy Technical Summaries). 

A:  Simply type the name of the bitmapÐfor example, NXscrollDownÐin the "Icon:" field of the Button inspector.  You'll need to make sure that the Icon Position control in the inspector allows an icon name to be typed.

In Release 2, if the word "Title" is indicated in the center of the diamond instead of "Icon," click the center to change it to "Icon" before typing the bitmap's name in the "Icon" field.

In Release 3, you need to select an Icon Position that includes a small square in the Button inspector.

If you forget which bitmaps are available or what their names are, you can quickly locate their pictures and names by searching the technical documentation in Digital Librarian.  (Search for the name of one of the bitmaps that you do remember, such as one of those in the Icons suitcase.)

Note that the system bitmaps table also specifies two cursor bitmaps, NXArrow and NXIBeam.  These can't be used in a cell, so InterfaceBuilder won't accept their names in the "Icon:" field.

Note that in Release 3, the system bitmap documentation can be found on-line under /NextLibrary/Documentation/NextDev/GeneralRef/ApD_SystemBitmaps/SysBitmaps.rtfd. Also, the Icons suitcase is now named "Images".

Q:  I am writing a simple editor program, which loads in a nib file for each document created.  I noticed with MallocDebug that there is a consistent leak of  22 bytes for every newly created document window, even though the documentation says that the default window is to be freed when closed.  Why is it leaking?

A:  The default window created with InterfaceBuilder is somewhat different from the default window created with the methods alloc and initContent:style:backing:buttonMask.  The InterfaceBuilder default window is not automatically freed when closed.  To fix this problem, simply check the "Free when closed" option in the Window Attributes Panel, using the InterfaceBuilder Projects inspector.

Q:  What is the "file's owner" of a .nib file?

A: The .nib's file's owner is an object that's external to the .nib file that's the conduit between objects in the .nib file and the other objects in your application. 

Each .nib file has one, and only one, owner.  For small applications, the owner is generally NXApp, the application object itself, although it can be an object of any class.  The owner is the only external object that may be the explicit target of action messages from Controls within the .nib file.  The owner may also have outlets that are initialized at runtime to the id's of objects within the .nib file. 

A .nib file is actually an archived collection of objects (windows, panels, controls, etc) that you designed in InterfaceBuilder.  When that file is unarchived at runtime and those objects are loaded, some object needs to "own" them.  For example, the main menu is loaded and owned by NXApp.  When you only have one .nib file, actually all objects are loaded and owned by NXApp.  When you have multiple .nib files (like /NextDeveloper/Examples/Draw), you explicitly set the owner of each file.  The owner is like the "shepherd" of the objects in its file.  The owner sends messages to its objects (makeKeyAndOrderFront: to panels, for example) and it also can receive messages from its object (a button that sent its target method "quit:" to the file's owner, for example).  As usual, you make these connections in InterfaceBuilder. 

The file's owner is the only link between those objects and objects elsewhere in the other .nib files of this application.  For example, you could create an info panel in a separate .nib module, and have a custom object Controller which owns that .nib file.  Your main menu item "Info..." is in the main .nib file and doesn't know about the Info panel because it is in a separate .nib file.  However, the main menu can send a message to the Controller object, which, in turn, sends a message to the info panel telling it to display itself.

Why would you choose to use separate .nib files?  At launch time, your entire .nib file (all the panels, menus, buttons, whatnot) is unarchived.  This may inefficiently squander your precious time and memory.  If you have a panel that is sparsely used, you may instead want to place it in a separate nib module and load it when, and if, it is needed.

InterfaceBuilder modules also allow you to create custom panels, windows, and objects that are reusable.  You could place the panel in a nib module and design a custom object that will control and own it, and then by instantiating that object and reusing the .nib module file, you can add that panel and its functionality to any application you create.

In NEXTSTEP Release 2, look at the two-.nib-file example of the Text Editor in the InterfaceBuilder tutorial (Chapter 8 of the NeXTstep Concepts manual) as a guide.

In NEXTSTEP Release 3, the InterfaceBuilder tutorial is covered in Chapter 17 of the Development Tools and Techniques book (the on-line location is /NextLibrary/Documentation/NextDev/DevTools/17_TextApp).

In all versions, see /NextDeveloper/Examples/Draw for a more advanced use.

Q:  I have a class that I have made into a palette and I want to include it in several other palettes, either as itself or in conjunction with a subclass. Yet, when I try to load two palettes that contain my class, the second one produces a load error in InterfaceBuilder. If I only include the code for my class in the first palette, the second palette loads, but only if the first palette has been previously loaded (this is unacceptable!). How can I have this class exist in multiple palettes? 
	
A:  The problem is that palettes, like bundles, keep all of your classes in one object file inside of the palette file package. When the system tries to dynamically link this object file into an application (in this case InterfaceBuilder), it fails if it runs across any previously defined symbols (this is the case for classes that are linked into several different palettes). Failure to load the object file is not a fatal error. However, even though the multiply defined object in your palette may already linked into your application (say from a previously loaded palette) there were probably other classes that weren't loaded (ie, a subclass).

The solution is to try to load any classes that may exist in other palettes separately. You can do this by adding a new bundle subproject to your palette project (choose New Subproject... from the Project menu item, then set the type to Bundle from the pop-up list). Add your class (with any support classes, like its inspector) to this bundle project. You also have to add a new bundle subproject for any classes that depend on that class (i.e., subclasses).  Finally, you need to add the following code to your subclass of IBPalette (which is created for you automatically when you create a new palette project) to load the (now separate) pieces:
	
@implementation  MyPalette : IBPalette

- init
{
	char path[MAXPATHLEN];	// Buffer to store path to bundles
		
	// Do super init
	[super init];
	
	// Get path for bundle suproject called "MyClass"
	[[NXBundle bundleForClass:[self class]] getPath:path 
		forResource:"MyClass" ofType:"bundle"];
	
	// Create NXBundle for "MyClass" and load its object file. Ignore return
	//  value (assume that failure means class was already loaded).
	[[[NXBundle alloc] initForDirectory:path] principalClass];
	
	// Get path for bundle subproject called "MySubclassOfMyClass"
	[[NXBundle bundleForClass:[self class]] getPath:path
		forResource:"MySubclassOfMyClass" ofType:"bundle"];
		
	// Create NXBundle for "MySubclassOfMyClass" and load its object file
	if(![[[NXBundle alloc] initForDirectory:path] principalClass])
		NXRunAlertPanel("MySubclassOfMyClass Load Error",
			"Now something else went wrong!", "OK", NULL, NULL);
	
	return self;
}

@end