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 funny 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, Push 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:
- Sliders
- Date Pickers
- 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! :]
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 an NSSlider.
All four types of sliders (horizontal, vertical, radial and slider cell) 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 four types, as shown below:
Slider Semantics
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 properties are outlined here:
// getting & setting an integer value let theInteger = mySlider.integerValue mySlider.integerValue = theInteger // getting & setting a float value let theFloat = mySlider.floatValue mySlider.floatValue = theFloat // getting & setting a double value let theDouble = mySlider.doubleValue mySlider.doubleValue = theDouble // getting & setting the minimum value of the range let theMinimumValue = mySlider.minValue mySlider.minValue = theMinimumValue // getting & setting the maximum value of the range let theMaximumValue = mySlider.maxValue mySlider.maxValue = 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 an NSSlider
in your app!
Pick a Number, Any Number
You could have the user just type in a value into a text field, but it’s much more interactive – and intuitive – to let the user use a slider to enter a numeric 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: [10]. 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 outlets; 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.swift file is selected — Ctrl-Drag the label to the RootViewController.swift to create a new outlet. Name it amountLabel.
Repeat the above process with the slider, naming the outlet amountSlider.
Since you want the slider to inform the app whenever its value is changed, you need to add an action to your application. You already created an action for your button in part 1; adding an action is very much like creating an outlet, so you’ll get a little more practice!
To create an action in your code, select the slider and Ctrl-Drag to RootViewController.swift anywhere within the class definition:
In the popup window, be sure to set the connection as an action rather than a outlet. 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:
let amount = amountSlider.integerValue amountLabel.stringValue = "Amount: [\(amount)]" |
A quick review of the code above shows that you first read the slider’s current value. Then you set the value of the label to a string containing the slider’s value.
Note: This example uses integerValue to get a nice round number, 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:
// Update the amount slider sliderChanged(self) |
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 properties to do this are set out below!
// getting & setting the date/time value let myDate = myDatePicker.dateValue myDatePicker.dateValue = myDate // getting & setting the minimum date of the range let theMinimumDate = myDatePicker.minDate myDatePicker.minDate = theMinimumDate // getting & setting the maximum date of the range let theMaximumDate = myDatePicker.maxDate myDatePicker.maxDate = 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 an outlet 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 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 datePicker.dateValue = NSDate() |
Build and run the app! You should see your shiny new date picker displaying current date, like in the screenshot below:
Everything working OK? Great! Next up: 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:
There’s a special class of control to handle this unique grouped behaviour: 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 and properties:
// Select a radio button at a specific row and column within the matrix let row = 3 let col = 1 myMatrix.selectCellAtRow(row, column: col) // Get the selected row of the matrix let selectedRow = myMatrix.selectedRow // Get the selected column of the matrix let 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 3, and Columns to 1. Change the title of the first radio button to read WWDC, the title of the second one to read 360iDev, and the third to read RWDevCon, 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.swift source file, just below the existing properties. Be careful to select and drag from the group, and not an individual radio button!
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 RWDevCon 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 placeRadioButton.selectCellAtRow(2, column: 0) |
Build and run the application. You should see your radio button control on the window, ready to be selected, and the RWDevCon 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 otherwise. 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.state = NSOnState // Set the state to Off myCheckBox.state = NSOffState // Get the state of a check box let 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 an outlet for the check box. Open the assistant editor and Ctrl-Drag the check box to RootViewController.swift 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 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 an outlet to set the value of this new label to contain your new hilarious sentence! As before, Ctrl-Drag the label to the RootViewController.swift 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 it in just a bit. The last control you’ll need — an Image View, to show your image on the screen — is up next.
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 are very few properties you need to interact with an Image View at runtime:
// Get the image from an image view let myImage = myImageView.image // Set the image of an image view myImageView.image = 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 outlet for the image view in the same way you’ve done for all the previous controls: Ctrl-Drag the image view to the RootViewController.swift 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, and add the single image to your project.
When the options window appears, make sure the Destination: Copy items 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 let pastTenseVerb = pastTenseVerbTextField.stringValue // Singular noun let singularNoun = singularNounCombo.stringValue // Place let cell: NSButtonCell = placeRadioButton.selectedCell() as NSButtonCell let placeString = cell.title // Amount let amount = amountSlider.integerValue // Plural noun let pluralNoun = pluralNouns[pluralNounPopup.indexOfSelectedItem] // Phrase let phrase = phraseTextView.string // Date let dateFormatter = NSDateFormatter() dateFormatter.dateStyle = NSDateFormatterStyle.LongStyle let date = dateFormatter.stringFromDate(datePicker.dateValue) // Speak or SHOUT var voice = "said" if yellCheck.state == NSOnState { voice = "yelled" } // Create the mad lib sentence let results = "On \(date), at \(placeString) a \(singularNoun) \(pastTenseVerb) \(amount) \(pluralNoun) and \(voice), \(phrase)!" // Display the mad lib sentence resultTextField.stringValue = results // Load the rage face image imageView.image = NSImage(named: "face.png") |
That may seem like a lot of code, but don’t worry — it will be explained step by step! :]
let pastTenseVerb = pastTenseVerbTextField.stringValue |
Here you’re getting the string value from the pastTenseVerbTextField by calling its stringValue property.
let singularNoun = singularNounCombo.stringValue |
In this section of code, you get the string from the combo box by calling its stringValue property.
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.
let cell: NSButtonCell = placeRadioButton.selectedCell() as NSButtonCell let placeString = cell.title |
Here you get the currently selected radio button by calling NSMatrix’s selectedCell method, and assigning the title string value to the placeString constant.
let amount = 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 floatValue or doubleValue.
let pluralNoun = pluralNouns[pluralNounPopup.indexOfSelectedItem] |
Here you get the plural noun, selected from the popup button. How is this done? Look up the appropriate plural noun in your pluralNouns
array using array subscript syntax, by calling the popup indexOfSelectedItem property.
let phrase = phraseTextView.string ?? "" |
Next up is the phrase the user typed. To acquire it, simply retrieve the string value of our text view by getting its string property. Again, you’re using nil coalescing since the property is an optional and could be nil.
let dateFormatter = NSDateFormatter() dateFormatter.dateStyle = NSDateFormatterStyle.LongStyle let date = dateFormatter.stringFromDate(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.
var voice = "said" if 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, leave it as the default said.
let results = "On \(date), at \(placeString) a \(singularNoun) \(pastTenseVerb) \(amount) \(pluralNoun) and \(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. The results constant uses string interpolation, and the different values read from the various controls to build a string, based on the user’s input.
resultTextField.stringValue = results imageView.image = NSImage(named: "face.png") |
Finally — you can display the results of all your hard work! First, display the sentence in the results label, by setting its stringValue property. 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 OS X 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 the final 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
- Attributed String Programming Guide
- Matrix Programming Guide
- Combo Box Progamming Topics
- Application Menu and PopUp Programming Topics
- Image Kit Programming Guide
I hope you enjoyed this tutorial, and as always if you have any questions or comments please join the forum discussion below!
Introduction to OS X Tutorial: Core Controls and Swift Part 2/2 is a post from: Ray Wenderlich
The post Introduction to OS X Tutorial: Core Controls and Swift Part 2/2 appeared first on Ray Wenderlich.