Configuring the Window
Xcode provides a library of objects that you can add to a window or view. The TrackMix app already contains a window—now you need to add a text field, slider, and button. Then, you make connections between these elements and your classes, and implement the behavior you want in the next chapter.
Add the UI Elements
You add UI elements by dragging them from the object library to a view or window. After the UI elements are added, you can move, resize, and configure them as needed.
In the project navigator, under the TrackMix group, select the
MainMenu.xib
file to display the TrackMix window.If necessary, click the rightmost View button in the toolbar to display the utility area at the right side of the window.
If necessary, click the window object icon in the editor area to display the window.
In the editor area, make the window narrower by dragging the three dots that appear adjacent to the window’s corners and edges.
If necessary, open the object library.
The object library appears at the bottom of the utility area. If you don’t see the object library, you can click its button, which is the third button from the left in the library selector bar:
In the object library, choose Cocoa > Controls from the Object Library pop-up menu.
Xcode displays a list of controls below the pop-up menu. The list displays each control’s name, an image, and a short description of its function.
One at a time, drag a text field, a vertical slider, and a push button from the list and into the window.
You can use the search field at the base of the utility area to find the objects you want and drag them to the window.
You resize the items using resize handles, where appropriate, and reposition them by dragging. As you move items within the view, alignment guides are displayed as dashed blue lines.
Drag the text field so that it’s near the top of the window and centered horizontally.
As you move the text field (or any UI element), dashed blue lines—called alignment guides—appear that help you align the item with the center and edges of the view. Stop dragging the text field when you can see the view’s center and upper alignment guides, as shown here:
Drag the vertical slider so that it appears centered and below the text field.
In the view, prepare to resize the vertical slider.
You resize a UI element by dragging its resize handles, which are small white squares that can appear on the element’s borders. In general, you reveal an element’s resize handles by selecting it. If your vertical slider looks like the one below, you’re ready to resize it; if it doesn’t, select it on the canvas.
Drag the vertical slider’s lower resize handle until the window’s center alignment guide appears.
Stop resizing the text field when you see something like this:
Drag the button so that it’s near the bottom of the window (below the vertical slider) and centered horizontally.
After you add the text field, vertical slider, and push button UI elements and make the recommended layout changes, your project should look similar to this:
You can specify some of the UI elements’ behavior and appearance using the Attributes inspector. Add a default string 5
to the text field, configure the vertical slider to have minimum and maximum values, and set the title of the button to Mute
.
Click the text field to select it and open the Attributes inspector (if necessary) by clicking the icon that looks like this:
In the Title text field at the top of the Text Field Attributes inspector, enter
5
.This is the initial value of the text field. Alternatively, double-click the text field in the TrackMix window and enter the number directly.
Click the Align Center button to center the text field’s text.
After you enter the text and change the alignment setting, the Text Field Attributes inspector should look like this:
In the editor area, select the slider.
In the Value section’s Minimum field of the Slider Attributes inspector, enter zero (
0
).In the Value section’s Maximum field, enter
11
.In the Current Value field, enter
5
(to match the text field).Scroll down to the Control attributes (if necessary) and select the Continuous checkbox.
If the slider is continuous, it sends messages while it’s being moved rather than only when a new value is set.
After you change the slider setting, the Slider Attributes inspector should look like this:
Do one of the following:
Double-click inside the button, type
Mute
, and press Tab.Select the button, type
Mute
in the Title field of the Button Attributes inspector, and press Tab.
Optionally, click the Run button (or choose Product > Run) to make sure that the UI elements you added look the way you expect them to. You should see something like this:
You can interact with the UI elements but at the moment, the Mute button doesn’t do anything, the text field input doesn’t move the slider’s knob, and the slider doesn’t change the value displayed by the text field. To add this functionality, you need to make the appropriate connections between the UI elements and write some code. These connections are described next.
Create Actions for the Button, Text Field, and Slider
When the user activates a UI element, the element can send a message to an object that knows how to perform the corresponding action. This interaction is part of the target-action mechanism, a Cocoa design pattern.
In this tutorial, when the user clicks the Mute button, you want it to send a “set the track’s volume to zero” message (the action) to the app delegate (the target). In this case, the app delegate acts as a view controller in the Model-View-Controller design pattern. This action message changes the “model” data (the track’s volume) that the app delegate manages. Then, the app delegate updates the other views (the slider and text field) to reflect the change in the model object’s data. You’ll create the model object that represents the track later.
Using Xcode, you can set an action for a UI element and set up its corresponding action method by Control-dragging from the element on the canvas to the appropriate source file (for example, to the AppDelegate
class source file). The connections that you create in this way are archived, along with the UI elements, in the nib file. Later, when the app loads the nib file, the connections are restored.
If necessary, select
MainMenu.xib
in the project navigator to display the app window.In the Xcode toolbar, click the Utilities button (the rightmost View control) to hide the utility area and click the Assistant editor button to display the assistant editor pane.
The Assistant editor button is the middle Editor button and it looks like this: .
Make sure that the assistant editor displays the
AppDelegate
class header file (that is,AppDelegate.h
).Select
AppDelegate.h
from the Top Level Objects menu at the top of the assistant editor.On the editor, Control-drag from the Mute button to the method declaration area in
AppDelegate.h
(that is, between the@interface
statement and the@end
statement).To Control-drag, press and hold the Control key while you drag from the button to the header file in the assistant editor pane.
When you release the Control-drag, Xcode displays a popover in which you can configure the action connection you just made.
In the popover, configure the button’s action connection:
In the Connection pop-up menu, choose Action.
In the Name field, enter the method name
mute:
.In a later step, you’ll implement the
mute:
method to set the volume to zero.Make sure that the Type field contains
id
.The
id
data type can represent any Cocoa object. You want to useid
here because it doesn’t matter what type of object sends the message.
After you configure the action connection, the popover should look like this:
In the popover, click Connect.
Xcode adds a method declaration to the header file and a stub for the new method to the implementation file. Xcode indicates that the connection has been made by displaying a filled-in circle to the left of the method:
Control-drag from the text field to the method declaration area in
AppDelegate.h
.In the popover that appears when you release the Control-drag, configure the text field’s action connection:
In the Connection pop-up menu, choose Action.
In the Name field, enter
takeFloatValueForVolumeFrom:
.In a later step, you’ll implement the
takeFloatValueForVolumeFrom:
method to set the model data and synchronize the text field with the slider.Make sure that the Type field contains
id
.
In the popover, click Connect.
Xcode adds a method declaration to the header file and a stub for the new method to the implementation file. Xcode indicates that the connection has been made by displaying a filled-in circle to the left of the method:
You accomplished two things by Control-dragging from the button and text field to the AppDelegate.h
header file:
You created a connection between the UI element and the app delegate object.
For example, when the nib file is loaded, the button’s target is set to the app delegate object and its action is set to the
mute:
message so that when the button is clicked, it sends themute:
message to the app delegate object. (In Objective-C, objects send messages to each other and the Objective-C runtime determines which method implementation to execute.)You added the corresponding code to the
AppDelegate
class. Specifically, you added the following action method stub implementations toAppDelegate.m
and corresponding declarations toAppDelegate.h
:- (IBAction)mute:(id)sender {
}
- (IBAction)takeFloatValueForVolumeFrom:(id)sender {
}
Control-dragging from a UI element to source code is a convenient shortcut for creating a connection to an object and adding an action or outlet to its class . If the action or outlet already exists, you Control-drag directly to or from it.
Because the slider can use the same takeFloatValueForVolumeFrom:
method as the text field uses, you simply connect it to the app delegate object to set the slider’s target and action. You can do this because the action method was added to the AppDelegate
class in a previous step.
In the editor area, Control-drag from the slider to the App Delegate blue cube that appears on the left side of the editor area below the window icon (the blue cube represents an object).
Xcode displays a list of action methods implemented by the
AppDelegate
class.When you release the Control-drag, select the
takeFloatValueForVolumeFrom:
method to make the connection.
Create Outlets for the Text Field and Slider
An outlet describes a connection between two objects. When you want an object (such as the app delegate) to communicate with an object in the nib file (such as the text field), you designate the contained object as an outlet. When the nib file is loaded at runtime, the outlet you create in Xcode is restored, which allows the objects to communicate with each other.
In this tutorial, you want the app delegate to get the user’s text from the text field, set the volume accordingly, and update the slider. To allow the app delegate to communicate with these objects, you create outlet connections for each. You need an outlet only for the text field and slider. You don’t need an outlet for the button because the app delegate doesn’t need to send messages to the button.
The steps you take to add outlets for the text field and slider are very similar to the steps you took when you added the button’s action. Before you start, make sure that the MainMenu.xib
file is still visible in the editor and that AppDelegate.h
is open in the assistant editor.
Control-drag from the text field in the window to the method declaration area in the interface file.
It does not matter where you release the Control-drag as long as it’s inside the method declaration area. In this tutorial, the new outlets appear below the window outlet declaration.
In the popover that appears when you release the Control-drag, configure the text field’s connection:
Make sure that the Connection pop-up menu contains Outlet.
In the Name field, enter
textField
.You can call the outlet whatever you want, but your code is more understandable when an outlet name bears some relationship to the item it represents.
Make sure that the Type field contains
NSTextField
.Setting the Type field to
NSTextField
ensures that Xcode connects the outlet only to a text field.Make sure that the Storage pop-up menu contains Weak, which is the default value for outlets.
After you make these settings, the popover should look like this:
In the popover, click Connect.
Xcode adds a declaration for the outlet and indicates that the connection has been made by displaying a filled-in circle to the left of the method:
Control-drag from the slider in the window to the method declaration area in the header file.
In the popover that appears when you release the Control-drag, configure the slider’s connection:
Make sure that the Connection pop-up menu contains Outlet.
In the Name field, enter
slider
.Make sure that the Type field contains
NSSlider
.Make sure that the Storage pop-up menu contains Weak.
After you make these settings, the popover should look like this:
In the popover, click Connect.
You accomplished two things by adding outlets for the text field and slider:
You established outlet connections from the app delegate to the text field and slider.
When the nib file is loaded, the app delegate’s
textField
outlet (an Objective-C property) is set to theNSTextField
object and theslider
outlet is set to theNSSlider
object. This allows the app delegate to directly get and set the values of these UI elements.You added the corresponding code to the
AppDelegate
class. Specifically, you added the following properties toAppDelegate.h
:@property (weak) IBOutlet NSTextField *textField;
@property (weak) IBOutlet NSSlider *slider;
Also, you added the following code lines to
AppDelegate.m
to synthesize these properties (a process that creates the corresponding get and set methods):@synthesize textField;
@synthesize slider;
At this point in the tutorial, you’ve created a total of five connections to the app delegate object:
An action connection for the button
An action connection for the text field
An action connection for the slider
An outlet connection for the text field
An outlet connection for the slider
You can verify these connections in the Connections inspector.
While viewing the
MainMenu.xib
file, click the Standard editor button to close the assistant editor and switch to the standard editor view.The Standard editor button is the leftmost Editor button and it looks like this:
Click the Utilities view button to open the utilities area.
Select App Delegate in the editor area (the blue cube located under the window icon).
Show the Connections inspector in the utilities area.
The Connections inspector button is in the inspector selector bar, and it looks like this:
In the Connections inspector, Xcode displays the connections for the selected object (in this case, the app delegate). In your workspace window, you should see something like this:
Notice the five connections (three actions and two outlets) you created to the app delegate. Since the text field and vertical slider use the same takeFloatValueForVolumeFrom:
action method they are grouped together under Received Actions. Also notice that there’s a connection between the app delegate and its window and the app delegate and the File’s Owner. Xcode provides these default connections for your convenience; you do not have to use them in any way.
Test the App
If you run your app now, it behaves the same as the last time you tested it after you first added the UI elements to the window. The connections between the objects exist but because the action methods do nothing, there is no new behavior to observe. So that you can test and better understand the connections you made, in the implementation file, AppDelegate.m
, add log statements to the action methods.
Display the implementation file in the source editor by selecting
AppDelegate.m
in the navigator area in the left pane.Add a single
NSLog
statement to themute:
method to indicate that the method has been invoked.- (IBAction)mute:(id)sender {
NSLog(@"received a mute: message");
}
Implement the
takeFloatValueForVolumeFrom:
method as follows:- (IBAction)takeFloatValueForVolumeFrom:(id)sender {
NSString *senderName = nil;
if (sender == self.textField) {
senderName = @"textField";
}
else {
senderName = @"slider";
}
NSLog(@"%@ sent takeFloatValueForVolumeFrom: with value %1.2f", senderName, [sender floatValue]);
}
The method checks which control was the sender and logs a message indicating both the sender and its current float value. The
NSLog
function logs a string using the format specified by the format string. (This is very similar to the Cprintf
function.) The string%@
indicates that a string object should be substituted.The method combines two fairly common patterns in Cocoa: determining the sender of a message and sending a message back to it. In this case, it’s not necessary to know which object sent the message—all that’s needed is its float value—but it serves to illustrate the point.
When you test the app, show the console so that you can see the log messages.
Click the Run button in the toolbar to build and run the project.
If a dialog appears asking whether to stop running TrackMix, click Stop.
If necessary, in the Xcode toolbar, click the debug area button to show the debug area.
The debug area button is the middle View control and it looks like this:
Click the rightmost button in the debug area view toolbar next to the Clear button to display the console.
Make sure that Target Output is selected from the console pop-up menu.
In TrackMix, enter a numeric value in the text field and press Return, move the slider, and click the Mute button.
As you interact with these UI elements, you should see appropriate messages in the console.
Recap
You created connections between the UI elements and the app delegate object by Control-dragging from the UI element to a AppDelegate
class source file and configuring the action or outlet. This operation performed two tasks: It created the connection between the objects and added the necessary code for the action or outlet to the source files.
You don’t have to use the Xcode feature that automatically adds code when you establish a connection by Control-dragging from the canvas to a source file. Instead, you can add the property and method declarations to the source files yourself and then make the connections from the UI elements directly to the App Delegate object in the editor area. Typically, though, you make fewer mistakes (and have less typing to do) when you let Xcode do as much of the work as it can.
Currently, the user interface is not synchronized (that is, the text field and slider may have different values). In the next chapter, you’ll implement the Track
class to separate the model data from the UI elements and in the following chapter implement the action methods.
© 2013 Apple Inc. All Rights Reserved. (Last updated: 2013-04-23)