a free
account from ADC. After installation, browse to your /Developer/Applications folder and
find Xcode, and open it.
After you open it, you should be presented with a simple setup assistant (use default settings)
as well as a "welcome screen" which can be safely closed out (or you can browse around it if
you feel the need to). Go to File->New Project (Shift - Command - N), and select Cocoa
Application.
In the next screen, name the project "My Cocoa Application" and press Finish. If you didn't
change the directory of the project at that screen, it's default location is ~/<Project Name>
(expanded: /Users/<user>/<Project Name>).
Now you are presented with the main Xcode window. On the left column is a list of all
relevant files: classes, which contain the classes of your project, other sources, such as the
main.m and prefix headers, resources, which include the interface document (MainMenu.nib
in this case), required frameworks, and products (the outcome of compiling your project, in
this case My Cocoa Application.app). The Target is the group of all files that make the
product.
Classes are similar to the source files of any application, however they can be used
interactively with each other. A method, which you will here about, is basically a function in
a class. A method is also a message sent to a class. A function can be called repeatedly from
different areas of code to do a repetitive set of instructions.
The first thing we want to do here is create a new class for controlling the program, aptly
named "Controller." Right click the Classes folder in the file list, and select Add->New File.
In the resulting window, select "Objective-C Class" from the list. In the next section, enter
"Controller" as the file name, and click Finish without changing any settings.
Now you should have had a new window open up, just close out of it. Right now we don't
need to edit any of these files. Now we can get on to making the interface. The interface file
is a .NIB file, which means NeXT Interface Builder. Un-disclose the "Resources" group in
the file list, and double click MainMenu.nib. Interface Builder, an app bundled with the
Developer Tools should now open up. At first you are presented with an empty window, a
document window listing the elements in the NIB, and a MainMenu bar. The empty window
is simply the window for your application, which as it is now doesn't have anything in it.
The Main Menu bar above is the same menu bar that will appear at the top of your screen
when your app is running. To make it say other than "NewApplication," double-click that
area, and type in "Cocoa Application." Now single-click the same spot, to open the menu.
Double-click each menu item to change it's name to match the application we're making. You
can also do the same for the item in the Help menu.
The Menu Bar's modification is complete, so you can close that window. Now we can add
some parts to the main window. Controls, or things you can interact with in a window
(buttons, sliders, lists, etc) are stored in the Library Palette. To bring up this palette, do Shift Command - L. You should see a grid of different controls you can put in your window, and
you can simply drag and drop them in. Let's drag a slider (NSSlider) and two text boxes
We need to actually instantiate the Controller class now. Do this by dragging an NSObject
(the plain blue cube) item from the Library palette straight into the document window.
Open the Inspector Palette (Command - Shift - I), and go to the Classes tab at the very top.
The Classes tab is the dark blue circle with the white "i" on it. You will see a label that says
"Class" with a drop-down box right next to it. In this drop-down box, type in the name of the
class you want it to be in this case the one we just instantiated - Controller. You will need
to instantiate custom classes before using them in this way.
Now you have a working instance of the controller class. Onto the next step, which actually
involves coding, finally. Don't close IB (Interface Builder) yet, though, we will need it soon
enough.
Go back to Xcode, and in the file list, select Controller.h. Go to View -> Zoom Editor In to
open up the in-window editor (or do Shift - Command - E). Right now in this header file you
should have this exactly:
#import <Cocoa/Cocoa.h>
@interface Controller : NSObject {
}
@end
Because this class will be interacting with the NIB file, we need to add IBOutlets, which
allow you to connect this object to some on-screen controls. For this, the IBOutlet type will
be id, which can stand for any object. Edit the header file (Controller.h) to look like this:
#import <Cocoa/Cocoa.h>
@interface Controller : NSObject {
IBOutlet id textField;
IBOutlet id slider;
IBOutlet id setToAmountField;
}
@end
Now you have three outlets in interface builder you can connect to the object. We still need to
make our methods, though. A method generally takes the following form:
-(<RETURN TYPE>)doThis:(id)arg1 andThis:(id)arg2; // (etc many arguments, or no arguments)
there can be
<RETURN TYPE> is the type of variable you want the method to return. For this application,
we will be using void which doesn't return anything. id is a variable type that, as you read,
stood for any object. You can replace it with some sort of variable that is the only variable or
class that can be passed into the method:
-(void)doThis:(NSString *)arg1 andThis:(NSString *)arg2;
The above method returns nothing, and can only take NSStrings as arguments.
There is a special kind of method setup for methods that are activated from an interface (with
controls, etc):
-(IBAction)doThis:(id)sender;
IBAction is just a notational way of doing void, but the (id)sender piece is required for
Interface Builder to properly connect the actions (methods). So... let's make a method.
The first method we want to do is a reset method, to reset everything back to zero, which will
be triggered by the Reset button. We also want to make a set-to method, but remember,
IBActions can only take one argument (sender). So we'll need to make two methods to
accomplish that. Edit your header file to look like this:
#import <Cocoa/Cocoa.h>
@interface Controller : NSObject {
IBOutlet id textField;
IBOutlet id slider;
IBOutlet id setToAmountField;
}
-(IBAction)reset:(id)sender;
-(IBAction)setToButtonClicked:(id)sender;
-(void)setAllTo:(int)numberToSetTo;
@end
The setToButtonClicked: method will later trigger the setAllTo: method. Right now, we can
hook these things up in Interface Builder. Connecting outlets and actions simply requires
Control - dragging from one object to another. To set one of objectA's outlets to objectB, you
drag from objectA to B. To have objectB activate one of objectA's methods, you drag from B
to A. Let's set the outlets of the Controller object. Go to interface builder, and control drag
from the blue Controller cube in the document window to the top text field, and in the
resulting HUD window, select the "textField" outlet. Do the same for the slider, and the
setToAmountField, which will be the text field right next to the Set To: button.
Of course, now we want to hook up our actions/methods. To do this, drag from the object that
will trigger the action to the Controller object, and select the proper action. For the Reset
button, select the reset: action, and for the Set to: button, select the setToButtonClicked:
action.
For the extent that this tutorial goes, the interface is done. You can close IB. However, it's not
a polished interface and some features that would be expected are missing. Head back to
Xcode. Copy those three methods we wrote in Controller.h to Controller.m, under
@implementation Controller so it looks like this:
#import "Controller.h"
@implementation Controller
-(IBAction)reset:(id)sender
{
}
-(IBAction)setToButtonClicked:(id)sender
{
}
-(void)setAllTo:(int)numberToSetTo
{
}
@end
Be sure to remove the semicolons from the end of those lines (just those lines). This .m file,
or implementation file, is where the instructions are mainly stored. Our header file tells us we
have these methods, and the implementation file shows us what they do.
Cocoa is full of predefined classes, 3 of which we have in our program here (NSSlider,
NSTextField, NSButton). These classes each have their own methods you can use to do
something to the class. Methods are sent in this fashion:
[receiver doThis:withTheseArgs];
The receiver is the object getting the message (doThis: and doSomething in these cases). We
are going to have three objects that need to be sent messages: slider, textField, and
setToAmountField. For slider and text field, we will be using one method (it works for both
classes - most classes do not share most methods): setIntValue:. It does just what it seems
like, it sets the integer value of the receiver. setToAmountField will be getting a different
message: intValue. Instead of setting the intValue, it gets it from the receiver. Let's look at
our reset: method.
We want this method to reset everything back to 0, so we will somehow be using
setIntValue:0 somewhere. There are two objects that will receive this: slider and textField.
Let's implement that under the method in the implementation file:
#import "Controller.h"
@implementation Controller
-(IBAction)reset:(id)sender
{
[slider setIntValue:50];
[textField setIntValue:50];
}
-(IBAction)setToButtonClicked:(id)sender
{
}
-(void)setAllTo:(int)numberToSetTo
{
}
@end
As you can see, it tells the slider and the textField to set their values to 50. However, we want
the user to be able to set it to a custom amount, which is what the other two methods are for.
We want the setButtonClicked: method to simply call the other method, setAllTo:, with an
argument (because IBActions cannot take any arguments other than sender). Since the
setAllTo: method is within the same class, we use self as the receiver. But remember, we
want to pass an argument that's the int value of the setToAmountField text field. So, what do
we do? Well, we call another method, as you read: intValue. This can all be done in one line
by separating with brackets, the standard Objective-C convention:
#import "Controller.h"
@implementation Controller
-(IBAction)reset:(id)sender
{
[slider setIntValue:50];
[textField setIntValue:50];
}
-(IBAction)setToButtonClicked:(id)sender
{
[self setAllTo:[setToAmountField intValue]];
}
-(void)setAllTo:(int)numberToSetTo
{
}
@end
That one line really calls two methods, and passes the result of one as the argument of
another. It doesn't do anything, yet, because setAllTo: is empty. We can basically use the
same code as reset: with a slight change, and that's using the passed argument:
numberToSetTo:
#import "Controller.h"
@implementation Controller
-(IBAction)reset:(id)sender
{
[slider setIntValue:50];
[textField setIntValue:50];
}
-(IBAction)setToButtonClicked:(id)sender
{
[self setAllTo:[setToAmountField intValue]];
}
-(void)setAllTo:(int)numberToSetTo
{
[slider setIntValue:numberToSetTo];
[textField setIntValue:numberToSetTo];
}
@end
However, the number formatter we did earlier does not work if the method simply grabs
whatever is in the text field at the time. So, we need to do an ifelse statement to determine
if the value is greater or equal than 0 or less than or equal to 100.
#import "Controller.h"
@implementation Controller
-(IBAction)reset:(id)sender
{
[slider setIntValue:50];
[textField setIntValue:50];
}
-(IBAction)setToButtonClicked:(id)sender
{
[self setAllTo:[setToAmountField intValue]];
}
-(void)setAllTo:(int)numberToSetTo
{
if(numberToSetTo >= 0 && numberToSetTo <= 100)
{
[slider setIntValue:numberToSetTo];
[textField setIntValue:numberToSetTo];
}
else
}
@end
As that is, it doesn't do anything at all if it's less than 0 or greater than 100, which would be
confusing. So, we are going to do something really easy to fix that. Right now, if you build
your application (with the Build and Go button in the toolbar), it will work, so you've made a
Cocoa app.
Change your code to this:
#import "Controller.h"
@implementation Controller
-(IBAction)reset:(id)sender
{
[slider setIntValue:50];
[textField setIntValue:50];
}
-(IBAction)setToButtonClicked:(id)sender
{
[self setAllTo:[setToAmountField intValue]];
}
-(void)setAllTo:(int)numberToSetTo
{
if(numberToSetTo >= 0 && numberToSetTo <= 100)
{
[slider setIntValue:numberToSetTo];
[textField setIntValue:numberToSetTo];
}
else NSBeep();
}
@end
else NSBeep(); means that if the value is not greater than 0 or less than 100, it will just make
a beep noise. NSBeep() is not a normal method, it's a global C function that can be called
from anywhere.