Core Controls in Mac OS X: Part 2/2
- Slipping and Sliding — NSSlider
- Pick a Number, Any Number
- Hot Date Tonight — NSDatePicker
- I’m Late for a Very Important Date
- Pushing Your Buttons — NSButton
- Buttoning Things Down — Adding a Button
- Video Killed the Radio…Button — NSMatrix
- A Place to Call Home – Adding Radio Buttons
- Ticking all the Boxes — NSButton
- Check and Double Check – Adding Checkboxes
- Pulling it All Together
- Room with a View — NSImageView
- Just a Pretty Face – Populating the Image Well
- Time To Make It Work
- Where To Go From Here?
In this final part of the tutorial, you’ll finish off your application, and learn how to use the following controls: sliders, date pickers, push buttons, radio buttons, check boxes, and image views.
Welcome back to the second and final part of the Core Controls in Mac OS X tutorial!
In the first part of this tutorial, you started building a Mad Libs style Mac OS X application, where the user enters various words and phrases in order to create a humorous sentence.
Along the way, you learned about and implemented some of the core UI controls that are used in OS X applications — namely, Text Fields, Combo Boxes, Pop Up Buttons and Text Views.
In this final part of the tutorial, you’ll finish off your application, and learn how to use the following controls:
- Date Pickers
- Push Buttons
- Radio Buttons
- Check Boxes
- Image Views
After finishing this two-part tutorial you’ll have a solid understanding of the core controls available on OS X, and you’ll be able to go forth and build some amazing apps for the Mac platform! :]
This tutorial will pick up where we left off last time – if you don’t have it already, here’s the project where we left things off.
Without further ado, it’s time to get to work!
Slipping and Sliding — NSSlider
A slider is a control that lets the user choose from a predetermined range of values. A slider has a minimum and a maximum value, and by moving the control’s knob, the user can choose a value between those two limits. Sliders can be either linear or radial. What’s the difference between the two, you ask ?
Linear sliders can be either vertical or horizontal, and they let you choose a value by moving the knob along the track. A really great example of linear sliders is in OS X’s Mouse preferences panel, as shown in the screenshot below:
Radial sliders are a little different — they are displayed as a small circle with a knob, which can be rotated a full 360 degrees. In order to select a value, you click and drag the knob to the required position. You can find a great example of radial sliders in Adobe Photoshop, where they’re used to define the angle of a gradient, as such:
The control responsible for this on OS X is a NSSlider.
All three types of sliders (horizontal, vertical and radial) are in fact the same control, NSSlider. The only difference is how they’re displayed. Interface Builder has an object in the Object Library for each of the three types, as shown below:
There are two common tasks you’re likely to perform when working with sliders: getting or setting the current value, and getting and setting the high and low limits of the slider’s range. These getter and setter methods are outlined here:
// getting & setting an integer value NSInteger myInteger = [mySlider integerValue]; [mySlider setIntegerValue:myInteger]; // getting & setting a float value float myFloat = [mySlider floatValue]; [mySlider setFloatValue:myFloat]; // getting & setting a double value double myDouble = [mySlider doubleValue]; [mySlider setDoubleValue:myDouble]; // getting & setting the minimum value of the range double theMinimumValue = [mySlider minValue]; [mySlider setMinValue:theMinimumValue]; // getting & setting the maximum value of the range double theMaximumValue = [mySlider maxValue]; [mySlider setMaxValue:theMaximumValue];
Again, nothing too surprising here — if you’ve learned anything by now, it’s that implementing standard UI controls in your OS X application is a fairly straightforward exercise. Move on to the next section to include a NSSlider in your app!
Pick a Number, Any Number
You could have the user just type in a value, but it’s much more interactive – and intuitive – to let the user use a slider to enter a value in your app.
To include a slider in your app, select MainMenu.xib in the project explorer to open it in Interface Builder, and then select the Main Window. In the Object Library palette, locate the Label control and drag it onto the window.
Double-click the control to edit its default text, changing it to Amount:. Next, locate the Horizontal Slider control and drag it onto the window, placing it to the right of the label.
Click on the slider to select it, and set the Minimum value to 1, and the Maximum value to 10, using the attributes inspector. Change the Current value to 5 — this will be the default value of the slider when the user first runs the app.
Also, make sure that Continuous is checked. It’s important that this option is set, as it tells the slider to notify of any change in the slider’s value. If Continuous wasn’t set, the slider would send only a single notification when the knob was moved!
The screenshot below shows the setup of the Slider control:
Now that the slider is in place, you’ll need to create two properties; one for the slider, and one for the label. Wait, you may say — that’s a little different. Why are you adding a property for the label?
That’s so you can update the label’s text continuously to list the current amount whenever the value of the slider is changed; hence why you set the Continuous property on the slider. Aha! Makes sense now, doesn’t it? :]
Select the assistant editor and — just as before, making sure the RootViewController.m file is selected — Ctrl-Drag the label to the RootViewController.m to create a new property. Name it amountLabel.
Repeat the above process with the slider, naming the property amountSlider.
Since you want the slider to inform the app whenever its value is changed, you need to add an action to your application. The method to add an action is almost identical to creating a property.
To create an action in your code, select the slider and Ctrl-Drag to RootViewController.m, but this time make sure to drop it inside the implementation of the class at any point below the @implementation RootViewController line, as shown below:
In the popup window, you’ll notice that the connection is an action rather than a property. Name it sliderChanged, like so:
Now that you’ve created the action, you need to add the code which will update the label whenever the action is called.
Add the following code inside the sliderChanged method:
NSInteger amount = [self.amountSlider integerValue]; NSString *amountString = [NSString stringWithFormat:@"Amount [%ld]", amount]; [self.amountLabel setStringValue:amountString];
A quick review of the code above shows that you first read the slider’s current value. Then you create and format a new string using the value read from the slider. Finally, you set the value of the label to the value of the newly created string.
Note: This example uses integerValue, but if you need more precision you could use either floatValue or doubleValue for your slider.
Build and run the app. Try moving the slider back and forth to see the label update with the slider’s current value:
There’s one small problem — did you notice it? The label does not display the slider’s current default value when the app first launches! While it’s not a big problem, it makes the app look unfinished. The reason for this is that the label is only updating when the slider’s knob is moved.
Fear not — it’s relatively easy to fix! :]
Add the following code to the end of awakeFromNib:
Now the app will call sliderChanged at launch — and that will cause the label to be updated, as the slider’s value is read even before the user touches the control. Neat!
Build and run your app — the label displays the value at first run, which is a small touch, but is one of those ‘fit and finish’ elements that make your app look polished.
What about more complicated values, such as calendar dates? Yup, OS X has those handled too! :]
Hot Date Tonight — NSDatePicker
Date Pickers are controls that display date and time values, as well as providing a method for the user to edit those values. Date Pickers can be configured to display a date, a time or both a date and time. The control responsible for this on OS X is NSDatePicker.
Date Pickers can be displayed in one of two styles: textual, where the date and time information is shown in text fields, and graphical, where the date is represented by a calendar and the time by a clock. You can find examples of all these styles in OS X’s Date & Time preferences panel, as in the screenshot below:
The most common tasks you’ll perform with a date picker are getting and setting the date or time value, and setting the minimum and maximum date or time values that are permitted in your control. The methods to do this are set out below!
// getting & setting the date/time value NSDate *myDate = [myDatePicker dateValue]; [myDatePicker setDateValue:myDate]; // getting & setting the minimum date of the range NSDate *theMinimumDate = [myDatePicker minDate]; [myDatePicker setMinDate:theMinimumDate]; // getting & setting the maximum date of the range NSDate *theMaximumDate = [myDatePicker maxDate]; [myDatePicker setMaxDate: theMaximumDate];
Again — the controls have a very simple getter and setter style interface to update these values. Now it’s time (pardon the pun!) to put this control to work! :]
I’m Late for a Very Important Date
Following the usual procedure, add a new label to your window and change its title to Date:. Find the Date Picker control in the Object palette, and drag it onto the window, placing it to the right of the label, like so:
Create a property for the date picker, just as you’ve done for each of the previous controls. In the popup window, name the property datePicker.
Just like the other controls in your app, it’s nice to display a default value to the user when they first run your application. Picking today’s date sounds as the default sounds like a good choice! :]
Add the following code to the end of awakeFromNib:
// Set the date picker to display the current date [self.datePicker setDateValue:[NSDate date]];
Build and run the app! You should see your shiny new date picker displaying current date, like in the screenshot below:
Pushing Your Buttons — NSButton
Buttons are controls designed to send a message to an app whenever they’re clicked by a user. An app associates an action with the button which is executed whenever the button is clicked.
The control responsible for this on OS X is NSButton.
On OS X there are many different styles of buttons which are viewable Interface Builder’s Object Library. They all work in much the same way, the only difference being their visual representation. The different types of Buttons are shown in the image below:
You should use the style of button that best suits your application’s design — refer to the OS X Human Interface Guidelines for advice and guidance on best design practices for your app.
Typically, when working with a button, you’ll simply need to associate an action with the button and set its title. However, there may be times when you need to disable the button, or change its appearance. The following methods allow you to perform these actions on your button:
// disable a button [myButton setEnabled:NO]; // enable a button [myButton setEnabled:YES]; // getting & setting a button's title NSString *theTitle = [myButton title]; [myButton setTitle:theTitle]; // getting & setting a button's image NSImage *theImage = [myButton image]; [myButton setImage:theImage];
Looks fairly simple — adding a button to your app in the next section should be a breeze!
Buttoning Things Down — Adding a Button
Find the Push Button in the Object Library palette and drag it onto the window, changing its title to Go!, like so:
Since you’re not going to manipulate the button in any way beyond letting the user click it, you don’t need to create a property for the button. However, you do need to create an action and associate it with the button, so that your app knows what to do when the button is pushed! :]
Just as before, Ctrl+Drag the button to the RootViewController.m file and release when it’s inside the class implementation, as in the screenshot below:
In the popup window that appears, name the action goButton.
The goButton: method will be invoked by the app whenever the user clicks on the button. Later, you will add a pile of code to this method, but for now it’s sufficient to add some debug code — just to make sure everything’s working!
Add the following code to goButton::
NSLog(@"Go! Button clicked");
That’s all the code you need for now — build and run your app! :] It should look like the screenshot below:
Every time you click the button, you should see the Go! Button clicked message appear in Xcode’s console.
Everything working OK? Great! Next up is a slightly different – but equally useful – style of button: Radio Buttons!
Video Killed the Radio…Button — NSMatrix
Radio buttons are a special type of control that always appear in groups; they are typically displayed as a list of options with selectable buttons alongside. Their behaviour is also somewhat unique; any button within the group can be selected, but selecting one button will deselect all other buttons in the group. Only a single button, within the same group, can be selected at one time.
A good example of radio buttons that are used to present a set of options is the iTunes Back Up options, as shown below:
In order to handle this unique behaviour group, a special class of control is used: NSMatrix. With NSMatrix, you can define a group of radio buttons and it will automagically handle all of the events of that group for you.
For example, every time a radio button is clicked, the matrix control selects the clicked button, and deselects the rest of the buttons within that group. You only need to worry about getting and setting the proper values. How convenient! :]
NSMatrix allows you to group radio buttons in rows and columns. When working with radio buttons and NSMatrix, you’ll typically need to get the row of the selected button, or select one of the buttons from your code. You can perform those actions using the following methods:
// Select a radio button at a specific row and column within the matrix NSInteger row = 3; NSInteger col = 1; [myMatrix selectCellAtRow:row column:col]; // Get the selected row of the matrix NSInteger selectedRow = [myMatrix selectedRow]; // Get the selected column of the matrix NSInteger selectedCol = [myMatrix selectedColumn];
Once again, a complicated control is reduced to some very simple methods. Read on to implement a radio button control in your app!
A Place to Call Home – Adding Radio Buttons
Add a new label to your app (you should be getting pretty comfortable with this by now!), and change its title to Place:. Locate Radio Group in the Object Library palette, and drag it onto the window, just beside the label.
Select the group of radio buttons and open the attributes inspector in the utilities panel. Set Rows to 2, and Columns to 1. Change the title of the first radio button to read WWDC and the title of the second one to read 360iDev, as in the image below:
Now, create a new property for the radio buttons group — another action you should be quite familiar with by now! Ctrl-Drag the group into the RootViewController.m source file, just below the existing properties, like so:
In the popup window that appears, name the property placeRadioButton, as below:
Again, you’ll need to select a default value for your radio button control when the app launches. To make the 360iDev radio button the default, you’ll need to know the row and column index of the radio button you want to set as default. As the index of both the rows and columns in the radio button control is zero based, that makes it row 1, column 0.
Add the following code to the end of awakeFromNib:
// Set the radio group's initial selection [self.placeRadioButton selectCellAtRow:1 column:0];
Build and run the application. You should see your radio button control on the window, ready to be selected, and the 360iDev as the default:
Radio buttons are one way to toggle values in your app, but there’s another class of controls that perform a similar function — check boxes!
Ticking all the Boxes — NSButton
Check boxes are in fact the same as push buttons, but they warrant their own section because they’re used in a different manner. Typically, push buttons are used to send a message to an action when clicked; you don’t necessarily care about their state. With check boxes however, you need to know about their state, but most of the time, you don’t care if or when they’re clicked! :]
You typically use check boxes in an app to display the state of some boolean value. That state tends to influence the app in some way such as enabling a feature, or changing a property of an object.
You can find a good example of check boxes in the Reminders app. The check box informs both the app and the user if a task has been completed, and you can toggle the state of the task by clicking on the checkbox, as below:
Working with check boxes is relatively easy; most of the time you’ll only be concerned with getting and setting the state of the control. The state of the check box can be one of three states: NSOnState (feature on everywhere), NSOffState (feature off everywhere) and NSMixedState (feature on somewhere, but not everywhere).
Here’s how you can use it:
// Set the state to On [myCheckBox setState:NSOnState]; // Set the state to Off [myCheckBox setState:NSOffState]; // Get the state of a check box NSInteger state = [myCheckBox state];
Super simple! Time to add a checkbox to your app. You should be pretty familiar with adding controls and properties to your app by now — if you’re up for the challenge, see if you can figure out how to add the control without looking at the steps below! :]
Check and Double Check – Adding Checkboxes
Find the Check Box in the Object Library and drag it onto the window, changing it’s title to Yell !! as in the image below:
As you’ve done many times now, add a property for the check box. Ctrl-Drag the check box to RootViewController.m and in the popup window, name the property yellCheck, like so:
For this tutorial, make the check box default to the off state when the app launches. To do that, add the following code to awakeFromNib::
// set check button state self.yellCheck.state = NSOffState;
Build and run the application! :] You should see the check box, and it’s state should be unchecked. Click it to see it in action:
Okay! You’ve finally added all the controls you need to create your funny mad lib sentences. All you’re missing is a way to collect the value of each control, combine those values into a sentence, and display the sentence on-screen!
Pulling it All Together
First, you will need to add the controls to your app where the results will be displayed. You’re going to use two controls: a label to display the complete sentence, and an image view to display a picture, which should liven up the user interface!
Find the Wrapping Label in the Object Library palette and drag it onto the window, just below the Go!! button. Make it look a little more attractive by using the attributes inspector to change the border of the label to Frame, which is the first of the four buttons.
After that, remove the default text of the label by double-clicking it, selecting the text and hitting backspace, like below:
You’ll need to create a property to set the value of this new label to contain your new hilarious sentence! As before, Ctrl-Drag the label to the RootViewController.m file, and in the popup window. name the property resultTextField.
Leave this control as it is for now; you’ll write the code that populates this control in just a bit. The last control you’ll need — an Image View, to show your image on the screen — is described below.
Room with a View — NSImageView
An Image View is a simple and easy to use control that — surprise! — displays an image. Bet you didn’t expect that! :]
There’s very few methods you need to interact with the Image View at runtime:
// Get the image from an image view NSImage *myImage = [myImageView image]; // Set the image of an image view [myImageView setImage:myImage];
At design time, you can configure the visual aspects: the border, scaling and alignment. Yes, these properties can be set in code as well, but it’s far easier to set them in Interface Builder at design time, as below:
Just a Pretty Face – Populating the Image Well
OK, time to add an image view to your application! In the Object Library palette, find the Image Well and drag it onto the window, just below the wrapping label. Feel free to resize the app window if necessary.
Create a new property for the image view in the same way you’ve done for all the previous controls: Ctrl-Drag the image view to the RootViewController.m file, and in the popup window name the property imageView.
Build and run. Your app should now look like this:
Phew! Your user interface is finally finished — the only thing that’s left to do is to create the code that will assemble your hilarious sentence and populate the image view that you added above!
Time To Make It Work
Now that you have all the controls you need to allow the user to select the various inputs to the application, you need to construct the sentence based on those inputs.
When the user clicks the Go! button, you’ll collect all the values from the different controls and combine them to construct the full sentence, and then display that sentence in the wrapping label you added previously.
Finally, in order to spice up your all-text interface, you will also display a picture in the image view that you added in the last section.
To do this, download the resources for this project, unzip the file, and add the single image to your project.
When the options window appears, make sure the Copy items into destination group’s folder (if needed) option is checked, as shown below:
Clicking Finish will add the image file to your project and make it available for your use!
It’s finally time to add the core of the application — the code which constructs the mad lib sentence! Since this needs to happen when the Go! button is clicked, you’ll add the necessary code to the goButton method.
Add the following code to goButton:
// Past tense verb NSString *pastTenseVerb = [self.pastTenseVerbTextField stringValue]; // Singular noun NSString *singularNoun = [self.singularNounCombo stringValue]; // Place NSString *placeString; NSInteger row = [self.placeRadioButton selectedRow]; if( row == 0) placeString = @"WWDC"; else if( row==1) placeString = @"360iDev"; // Amount NSInteger amount = [self.amountSlider integerValue]; // Plural noun NSString *pluralNoun = nil; NSInteger selectedIndex = [self.pluralNounPopup indexOfSelectedItem]; pluralNoun = self.pluralNouns[selectedIndex]; // Phrase NSString *phrase = [self.phraseTextView string]; // Date NSString *date = nil; NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; [dateFormatter setDateStyle:NSDateFormatterLongStyle]; date = [dateFormatter stringFromDate:[self.datePicker dateValue]]; // Speak or SHOUT NSString *voice = @"said"; if( self.yellCheck.state == NSOnState) voice = @"yelled"; // Create the mad lib sentence NSString *results = [NSString stringWithFormat: @"On %@, at %@ a %@ %@ %@ %@ and %@, %@!", date, placeString, singularNoun, pastTenseVerb, @(amount), pluralNoun, voice, phrase]; // Display the mad lib sentence [self.resultTextField setStringValue:results]; // Load the rage face image self.imageView.image = [NSImage imageNamed:@"face.png"];
That may seem like a lot of code, but don’t worry — it will be explained step by step! :]
NSString *pastTenseVerb = [self.pastTenseVerbTextField stringValue];
Here you’re getting the string value from the pastTenseVerbTextField by calling its stringValue method.
NSString *singularNoun = [self.singularNounCombo stringValue];
In this section of code, you get the string from the combo box by calling its stringValue method.
You might ask why you don’t just look up the selected row, and then retrieve the string associated with that row? Quite simply, it’s because the user can enter their own text into the combo box. So use the stringValue to get the current string, which could have been either selected or typed.
NSString *placeString; NSInteger row = [self.placeRadioButton selectedRow]; if( row == 0) placeString = @"WWDC"; else if( row==1) placeString = @"360iDev";
Here you get the currently selected radio button by calling NSMatrix’s selectedRow method, and assign the necessary string value to the placeString variable, based on the selected index.
NSInteger amount = [self.amountSlider integerValue];
Next, read the slider’s current value using its integerValue method. Remember that if you need more precision with this control, you could also use either floatValue or doubleValue.
NSString *pluralNoun = nil; NSInteger selectedIndex = [self.pluralNounPopup indexOfSelectedItem]; pluralNoun = self.pluralNouns[selectedIndex];
Here you get the plural noun, selected from the popup button. How is this done? First, get the index of the selected item in the popup by calling its indexOfSelectedItem method. Then, look up the appropriate plural noun in your pluralNouns array.
NSString *phrase = [self.phraseTextView string];
Next up is the phrase the user typed. To acquire it, simply retrieve the string value of our text view by calling its string method. It’s really as easy as that! :]
NSString *date = nil; NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; [dateFormatter setDateStyle:NSDateFormatterLongStyle]; date = [dateFormatter stringFromDate:[self.datePicker dateValue]];
To get the date, call the date picker’s dateValue method. Then, convert the returned date to a human-readable string using an NSDateFormatter.
NSString *voice = @"said"; if( self.yellCheck.state == NSOnState) voice = @"yelled";
The final piece of the jigsaw: should you speak or shout? Simply get the checkbox state: if it’s NSOnState, assign yelled to the string variable. Otherwise, set the string to said.
NSString *results = [NSString stringWithFormat: @"On %@, at %@ a %@ %@ %@ %@ and %@, %@!", date, placeString, singularNoun, pastTenseVerb, @(amount), pluralNoun, voice, phrase];
At this point, you’ve collected all the information you need to construct the mad lib sentence! This is where the magic happens. Use the different values read from the various controls to build a string, based on the user’s input.
[self.resultTextField setStringValue:results]; self.imageView.image = [NSImage imageNamed:@"face.png"];
Finally — you can display the results of all your hard work! First, display the sentence in the results label, by calling its setStringValue method. Then, add some pizazz to the app by displaying an image to the user, which is as easy as loading the image and setting the image property of the image view control.
That’s it! You’re done! Run and build the app, so you can construct some hilarious sentences for yourself!
Congratulations! You’ve finished building the Mad Libs application, and have learned a ton about the most common Mac OSX controls along the way.
Feel free to play with the controls, select different values, type funny nouns or verbs and see the results each time you click the Go! button, and see what funny stories you can create! :]
Where To Go From Here?
Here is a example project containing all the code from this tutorial.
In order to gain a deeper understanding of the controls provided by OS X, I recommend you have a read through the different programming guides available from Apple listed below, which contain a wealth of information about how to use the available controls.
In particular, I highly recommend you read the OS X Human Interface Guidelines. This guide explains the concepts and theories of user interfaces on OS X, and Apple’s expectations of how developers should use the controls and design their UI’s to provide a consistent and pleasurable experience. It’s essential reading for anyone intending to develop for the Mac platform, especially if they plan to distribute their applications via the Mac App Store.
Here are some useful links to reinforce or further explore the concepts you’ve learned in this tutorial:
- OS X Human Interface Guidelines
- Button Programming Topics
- Text Editing Programming Guide
- Matrix Programming Guide
- Combobox Progamming Topics
- Application Menu and PopUp Programming Topics
- Image View Programming Topics
I hope you enjoyed this tutorial, and as always if you have any questions or comments please join the forum discussion below!