CamelBones

An Objective-C/Perl bridge framework

 
 
Home / Documentation / Getting Started / Outlets
 
 

Getting Started - Outlets

This HowTo covers how to send messages to GUI controls, to alter their appearance, change selection state, and so forth. It assumes that you've already completed the previous HowTos, and builds on information found in them.

1. Preliminaries

 
Screen shot of buttons & text field
Two buttons and a text field in IB
click to enlarge
 

Create a new project, and add a text field and two buttons to the main window. Set the buttons' titles to "Say Hello" and "Say Goodbye," and connect them to Perl methods that use the NSLog() function to print the indicated output to the console. The details of doing all that were covered in previous HowTos, so they're not repeated here.

This HowTo will outline how to print the output to the text label, instead of just sending it to the console.

2. Create an outlet for the label

 
Screen shot of adding an outlet
Adding an outlet in IB
click to enlarge
 

The procedure for adding an outlet is very similar to that for adding an action. Open MainWindow.nib in Interface Builder, and double-click the blue cube labeled "File's Owner" to edit the class definition. In the Info panel, select the "Outlets" tab, and click the "add" button to add a new outlet.

Name the text label something imaginative like "textLabel". (The screen shot says "TextLabel", but ignore that - it hasn't been updated yet.)

3. Create a connection to the outlet

 
Screen shot of outlet connection
Connecting an outlet in IB
click to enlarge
 

In IB's main window, select the "Instances" tab. Holding down control, click on the "File's Owner" icon, and drag the mouse cursor to the text label. You'll see a line being drawn, that represents the connection you're creating.

In the Info panel, the "Outlets" tab in the "Connections" pane will have a list of outlets, including the one you added in step 2. Select that one, and click the "connect" button at the bottom of the panel to create the connection.

4. Create a Perl outlet and accessor methods

In HelloWindowController.pm, you'll find a section of code that makes the HelloWindowController class available to Cocoa. It declares the class' super class, and its properties. Every outlet is a property, but not all properties are outlets. A property can also be a simple instance variable, or accessed through Cocoa Bindings. But that's for later. Here's what the code looks like:

class HelloWindowController {
    'super' => 'NSObject',
    'properties' => [
                        'windowController',
                    ],
};

As this implies, the only property that's defined by default is called "windowController", which is an instance of NSWindowController. Declare a new outlet named "textLabel"; this is the business end of the connection you created above. The code should now look like this:

class HelloWindowController {
    'super' => 'NSObject',
    'properties' => [
                        'windowController', 'textLabel',
                    ],
};

The declaration above will create two accessor methods, if the current class does not already have them, called "textLabel" and "setTextLabel". It will also make them available to be called from Cocoa. When the NIB is loaded below, Cocoa's NIB loading machinery will call "setTextLabel" to make the connection.

5. Modify the sayHello and sayGoodbye methods

If you followed the instructions above, you should already have two Perl methods that respond to action messages sent by the two buttons, and each should have a call to the NSLog() function to send output to the console. For example, the "sayHello" method should look something like this:

sub sayHello : Selector(sayHello:) IBAction {
    NSLog("Hello");
}

Add a line of code at the beginning of the method to retrieve the $self and $sender parameters. If you don't know what $self is, this would be a great time to read Tom Christiansen's excellent OO tutorial, found in the perltoot POD document. $sender is a reference to the GUI widget that sent the "sayHello" message.

The textLabel widget that you created in Interface Builder is an instance of the NSTextField class. NSTextField, like most GUI widget classes, is a subclass of NSControl, and inherits the "setStringValue" method from that class. Instance methods are called for GUI widgets by treating the outlets connected to them as object references. So, the "sayHello" method should now look like this:

sub sayHello : Selector(sayHello:) IBAction {
    my ($self, $sender) = @_;
    $self->textLabel()->setStringValue("Hello");
}

Repeat the above, making similar changes to the sayGoodbye method as well.

6. Build and run your app

As always, the last step is to build and run your application. If you correctly followed all of the steps above, clicking on the buttons should change the text displayed in the text label widget.

If clicking on a button generates a "can't call method setStringValue on an undefined value" error, make certain that the outlet is correctly defined and connected to the GUI widget in Interface Builder.

7. Working with $sender

As mentioned above, $sender is a reference to the GUI widget that generated the action message being responded to. You can use $sender to work with that widget, just as if you'd created an outlet and connected it to that widget. To help illustrate this, rename the "sayHello" method from above to "saySomething." Do the same in Interface Builder, renaming the "sayHello" action to "saySomething," and connect both buttons to the "saySomething" action.

The NSButton class has a "title" method that returns the button's label, and you can use this method to determine which button was clicked, or even use the title directly to produce an output message, as in the following example. (This example assumes that you've given the two buttons the titles suggested above: "Say Hello" and "Say Goodbye".)

sub saySomething : Selector(saySomething:) IBAction {
    my ($self, $sender) = @_;

    my $output = $sender->title;
    $output =~ s/Say //;

    $self->textLabel()->setStringValue($output);
}

What's next?

Next: The Responder Chain