Objective C Tips
How to create alternating menu items
How to create alternating menu items in Cocoa such that when one is activated the other one is turned off.
Go to the interface builder first. For each of the menu items, in the Attributes tab of the properties setting window, make the state to be On for the default option. I haven't experimented with the Alternate box yet, that might also work. Make sure both of your menu items are enabled.
Go back to Xcode and to the controller class for your application. Add a boolean attribute/field in the class that will represent which alternative was chosen (or integer or enum for more than two alternatives). In the init function set its value to be the one corresponding to the menu item that is On by default.
For each menu item, add a function in the controller class
returning an (IBAction) and taking as parameter an (id) sender,
like
- (IBAction) setFirst: (id) sender;
In the implementation of this function, set the state of this
menu as On, represented by the number 1:
[sender setState:1];
Then you need to set the state of the other menu item as
Off. You can locate this menu item by going to its parent menu first
([sender menu]), which is of a class derived from NSArray. The
different menus items are children of this menu at indexes starting
from 0 for the first one. So for example, if the menu to be turned
off is the second one from the top (not counting the title of the
menu itself), you'd do
[[[sender menu] itemAtIndex:1] setState: 0];
Do not forget to set the value of your boolean/int/enum attribute appropriately and perform any other action associated with this option.
Go back to the interface builder and create a connexion from each menu item to the controller object in MainMenu.xib (hold the control down while dragging the mouse from the menu item to the controller object) and associate the action with the name of the function you just wrote for it.
How to solve circular class references
Suppose that class A needs a reference to class B, and class B
needs a reference to class A. One of them can be included or
imported in the other, for example, A.h could contain
import "B.h"
Objective C has a mechanism to avoid circular references, so if you
try to import A.h inside B.h, you'll get an error. So in the header
for B you need to declare A as a forward class:
@class A;
This solves almost all the problems, except that calling methods from
class A that have parameters will result in a warning. To get rid of
it - and you should because most warnings are errors that don't
prevent the program execution - you can include or import the header
A.h not in the header file for B as you normally would, but in the
implementation file B.m:
#include "A.h"
How to find interface widgets in the code and change their attributes
In the Interface Builder, in the Attributes tab, set the tag of all the widgets that you need to control from the code to a unique number. You need at least one of them in the window that is connected as an outlet to the controller object of your application. Suppose the known IBOutlet objects is knownButton.
Find the parent of the button with
NSView *frame = [knownButton superview];
This is normally of a class inheriting from NSArray. Find the number of
children it has with
count = [[frame subviews] count];
Then you need a loop with say i going from 0 to count-1 where you check the
tag being equal to the unique value widgetTag that you've set for the widget
you're trying to identify:
if ([[[part subviews] objectAtIndex: i] tag] == widgetTag)
newWidget = [[part subviews] objectAtIndex: i];
Then depending on the widget type, you can change some of the information it
displays. For example, the label on an action button can be changed with
[newWidget setTitle: @"New Title"];
How to make an application quit when you close the window
First, you need to create a delegate connexion from the object called File Owner in the .xib file and the object standing in for the controller class in your app with Ctrl-drag from the first to the second and then choosing delegate.
Then you need to add the following function to your controller class:
- (BOOL) applicationShouldTerminateAfterLastWindowClosed: (NSApplication *) theApplication {
return true;
}