Welcome back to our 3-part Mac OS X Development tutorial for beginner series!
This introductory series about building apps on OS X has covered a lot of ground. This is what you’ve already learned:
- Tooling: In part one you learned about the many facets of Xcode – and had a glimpse of how you could use it to start developing for OS X.
- App Anatomy: The second part covered a lot of the theory behind how OS X app are constructed – from the data layer, through the binary assets, to designing the UI.
In this third and final part, you’re going to dive deep into the world of OS X development, by creating your first ever app!
The app you’re going to build is a magic-8 ball app, to help you make all those difficult decisions in day-to-day life. You’ll start from scratch – first creating a “Hello World!” app before moving on to creating an app to solve all your problems!
Getting Started
Open Xcode and click Create a new Xcode project to start your new app. Choose OS X \ Application \ Cocoa Application:
Set the product name MagicEight, the language to Swift and ensure that the Use Storyboards is checked:
Choose a location on disk to save your project and then click Create to open your empty project.
Build and run MagicEight to check that everything is working:
Great – so the app runs, but it does absolutely nothing yet. It wouldn’t be right to start a new platform without creating “Hello World!” first…
Hello World!
The user interface for an OS X app is provided by the storyboard file, which you edit using interface builder. This allows you to design the UI in a visual manner, reducing the amount of complex code you have to write.
Open Main.storyboard by selecting it in the Project Navigator in the left-hand pane:
You’ll now see the storyboard open in the center panel:
The template storyboard contains three components:
- Menu Bar: Controls the menu that appears when the app runs.
- Window Controller: Manages the default window that appears. Uses one or more view controllers to manage its content.
- View Controller Responsible for a region of the window–both for display and handling user interaction.
During this introductory tutorial you’ll concentrate your efforts on the view controller.
Adding a Label
First up, you need to add a label to the view controller to display your welcome text. A label is actually a special type of NSTextField
that’s designed for displaying a single line of non-editable text–perfect for “Hello World”.
To add your first label:
- Click the icon in the toolbar to show the utilities panel on the right-hand side.
- Use the icon towards the bottom of the panel to open the Object Library. This, as the name suggests, is a catalog of things you can drag onto the storyboard canvas to construct your layout.
- Use the search box at the bottom to search for label.
- You’ll see the label object highlighted in the library.
Drag the label from the object library onto the view controller:
To change the text content of the label, select the Attributes Inspector tab of the Utilities panel, locate the Title field and enter Hello World!:
You use the attributes inspector to control the appearance and behavior of the different controls that OS X offers. Find the Font entry, and click the T icon to the right to open the font panel. Change the style to Thin and the size to 40. Click Done to save the changes:
Cast your eyes back over to the view controller canvas and you’ll see something strange:
Although you’ve adjusted the text and size of the label, it has remained the same size, causing nearly all of the text to disappear. Why is this?
Basic Auto Layout
You placed the label on the view controller’s canvas, but you haven’t actually told OS X how it should position it. The view controller appears at one size in the storyboard, but once it’s running in the app, the user can change the window size–so you need to specify how the label should be positioned for any window size.
This sounds like it might be a really difficult task, but luckily Apple has your back. OS X uses a powerful layout engine called Auto Layout, in which the relationships between different components of the view are expressed as constraints. These constraints are then interpreted at runtime to calculate the size and position of each element within the view.
Ensure that the label is selected in the view controller, and then click the Align button in the lower toolbar. Select Horizontally in Container, ensuring the value is set to 0 before clicking Add 1 Constraint to add the constraint:
This will ensure that the label will always appear in the center of the window, irrespective of its width. You’ll notice that some angry red lines have appeared in the storyboard:
This is because you’ve provided a constraint that specifies the location of the label in the horizontal direction, but you’ve not provided any information about the vertical axis yet.
Once again, ensure that the label is selected in the view controller, and this time click the Pin menu on the Auto Layout toolbar at the bottom. Enter 30 in the top constraint box and ensure that the I-bar beneath it is solid red. Set the value of Update Frames to Items of New Constraints before clicking Add 1 Constraint:
Immediately you’ll notice the view controller update—you can now see the entire label, and the angry red lines have changed to calming blue:
And that’s your “Hello World” app done (for now). Use the “play” button to build and run, and inspect your handiwork:
Congratulations! Not very personal though is it? In the next section you’ll discover how to make the traditional greeting a little more friendly.
Handling User Input
In this section you’re going to add a text field to allow the user to enter their name so that you can welcome them personally.
Control Layout
As you did before, use the object library to locate and then drag a Text Field and a Push Button onto the view controller. Position them above the “Hello World!” label:
Remember that placing the controls on the canvas isn’t enough for OS X to understand how you want them positioned as the window changes size. You need to add some constraints to convey your wishes.
Select the text field, and then Command-Click the button to select both simultaneously. Click the Stack icon on the Auto Layout toolbar at the bottom of the storyboard:
This has created a new stack view containing the text field and the button. A stack view automatically generates the layout constraints required to position the contained views in a line. You can use the attributes inspector to configure many common properties of the stack view.
NSStackView
has been in OS X since 10.9, but received a significant update in 10.11 (El Capitan)—in line with its introduction (UIStackView
) in iOS. Stack views are similar on both platforms, so you can check out the iOS tutorial on stack views to get up to speed. Or hang tight—there’s a tutorial on NSStackView
dropping in the next few weeks.Once you’ve started stacking it’s difficult to stop. This time, you’re going to stack your newly created stack view with the “Hello World!” label—in a vertical stack view.
Use the button to the left of the lower toolbar to show the Document Outline and then locate the “Hello World” control and the existing stack view. Command-click them to select both:
As you did before, use the Stack button on the bottom toolbar to create a new stack view:
While this new stack view is selected, open the Attributes Inspector and set the Alignment to Center X, and the spacing to 20:
This ensures that the label and the upper stack view are nicely centered and there’s a gap of 20 points between them.
A couple more bits of layout to complete before you can turn your attention to the task of handling user input.
The stack view handles the positioning of its content relative to each other, but it needs to be positioned within the view controller’s view. Select the outer stack view and use the Align auto layout menu to center it horizontally within its container:
And use the Pin menu to pin the stack view to the top, with a spacing of 30:
Finally, to ensure that the text field always has the space for the user’s name, you will fix its width.
Select the text field and use the Pin menu in the bottom toolbar to specify a Width of 100. Click Add 1 Constraint to save the new constraint:
With that your layout is pretty much complete—it should look like the following:
Now you can turn your attention back to those new controls you added.
Outlets and Actions
Select the text field and open the Attributes Inspector. In the Placeholder field type Name:
This grayed out text will instruct the user what the field is for, and disappears as soon as they start typing.
Select the button, and again open the Attributes Inspector. Set the Title to Welcome:
That’s the cosmetic aspect done. Time to wire these two controls into code.
OS X provides a way to interact with the storyboard UI from code using outlets and actions:
- An outlet is a property in code that is connected to a component in the storyboard. This allows you to access the properties of controls within a storyboard from your code.
- An action is a method on a class in code that is invoked when the user interacts with the components in the UI – e.g. by clicking on a button.
You are going to add outlets for the text field and the label, and an action that will be called when the user clicks the welcome button.
The view controller you’ve been working on already has a skeleton class associated with it in code—in ViewController.swift. This is where you’ll add the outlets and action.
Open the assistant editor using the button in the toolbar:
This will split the screen and show a code file alongside the storyboard. It should be displaying ViewController.swift, but if it isn’t use the jump bar to select Automatic \ ViewController.swift:
Right-click-drag (or control-drag) from the text field in the storyboard over to the line above override func viewDidLoad()
in ViewController.swift:
Ensure that Outlet is selected and call the new outlet nameTextField:
This will add the following property to ViewController
and updates the storyboard to automatically connect the text field when loading the view controller:
@IBOutlet weak var nameTextField: NSTextField! |
Repeat exactly the same process for the “Hello World!” label, this time specifying that the outlet should be called welcomeLabel:
Now time to turn your attention to the button. Once again Control-drag from the button in the storyboard over to the ViewController
class:
This time, change the Connection to Action and name it handleWelcome:
Click Connect to make the connection and Xcode will add the following empty method to ViewController
:
@IBAction func handleWelcome(sender: AnyObject) { } |
This method will be called every time the user clicks the button.
Add the following line to the handleWelcome(_:)
method body:
welcomeLabel.stringValue = "Hello \(nameTextField.stringValue)!" |
This updates the stringValue
property of the welcomeLabel
to a welcome message constructed using the stringValue
of the nameTextField
. stringValue
represents the value currently displayed by a text-based control—be it user-entered or defined in the storyboard.
That’s all the code you need to get your personalized version of “Hello World” going! Build and run to give it a try. Once the app has started, try entering your name in the Name box and clicking the Welcome button. You’ll see your very own version of “Hello world!”:
Pretty cool eh? Well, the fun doesn’t stop there. Next up you’re going to learn about adding images to your app, to create an amazing magic-8 ball utility!
Assets
You were promised a magic 8-ball app at the start of this tutorial, and so far you’ve seen no mention of a ball. Well, that’s all about to change.
An 8-ball is pretty distinctive, and it wouldn’t be a very exciting 8-ball app without some visualization. Download the images you’ll need to make your app look swish. You’ll find two images inside the zip file—representing the two sides of a magic 8-ball:
Image assets are stored in an asset catalog within an OS X app. This catalog manages the different imagery and the different resolutions required for the app icons and the in-app images.
Open Assets.xcassets by selecting it in the project navigator:
You can see that the catalog currently only contains one item – AppIcon. This is where you would put the artwork to give your app a cool icon.
You need to add the downloaded images to the asset catalog. Locate the images in finder, and drag them both into the asset catalog:
This will create two new entries in the asset catalog:
Notice that there are three “cells” for the images. These cells represent the different screen scales–standard (1x), retina (2x) and retina-HD (3x). Normally you would provide assets for each of these cells, but in this simple project you will provide just one.
The image you’ve been provided is actually designed to be used at retina resolutions (2x), so drag the image from the left-hand cell to the central (2x) cell:
Repeat for both of the 8-balls assets, so that the asset catalog looks like this:
Those images are now available to use in your app—both from code and inside the storyboard.
Displaying Images
You’ve already seen how to display text, buttons and text input fields in your app, but how about images? Enter ImageView
.
Open Main.storyboard and use the object library to search for image view:
Drag an image view onto the view controller canvas, at the bottom of the stack view. Notice how a horizontal blue line will appear denoting where in the stack view that the new image view will appear:
Open the Attributes Inspector and set the Image property to 8ball:
This will update the canvas with the image, read from the asset catalog:
Build and run the app to see how the image appears:
Well, the image has appeared, but is clipped by the window. You can drag to resize the window in the usual way, but it would be much better if the window was correctly sized when the app starts, and then keeps a fixed size.
Window Size
The initial size of MagicEight’s window is determined by the size of the view controller in the storyboard. Select the view controller’s view in Main.storyboard and open the Size Inspector. Set the Width to 350 and the height to 480:
Notice that the storyboard canvas now looks strange:
This is because the layout constraints need to be re-evaluated.
Use the Resolve Auto Layout Issues menu on the bottom auto layout menu bar and select All View in View Controller \ Update Frames:
This will re-run the layout engine over the storyboard, and update the preview:
Build and run to see how the app looks now:
That looks much better. Try resizing the window—you’ll see that you can still manually drag it to sizes that don’t work for MagicEight’s layout.
You have a couple of options here—you can either update the layout so that it adapts nicely as the window changes size (e.g. to resize the image for small windows), or you can fix the window size. You’re going to take the second, easier approach.
Window Sizing
The window controller is responsible for managing the size of the window. Open Main.storyboard and select the window inside the window controller:
Open the Size Inspector and set the Content Size to a Width of 350 and a height of 480. Then check Minimum Content Size and Maximum Content Size ensuring that the the sizes are the same as the content size you entered:
Build and run MagicEight again, and attempt to resize the window. You no longer have control over the window size—it’s now fixed. Mega—just what you wanted.
You can now turn your attention back to the task in-hand—building the “magic” of the magic 8-ball. Well, nearly—there’s a bit more layout to do first.
Handling clicks
When the user clicks on the 8-ball you will switch the image to the other side and display a piece of advice. You need a new label to display this advice.
Open Main.storyboard and find the view controller scene. Use the Object Library to locate a wrapping label and drag it from the library beneath the stack view:
You want this label to be positioned at the center of the magic-8 image, so you need to add some auto layout constraints.
Control-drag from the Multiline Label to the Image View in the Document Outline:
Hold shift and select Center Vertically and Center Horizontally before clicking Add Constraints:
The label doesn’t automatically reposition—but you’ve handled this before. Use the Resolve Auto Layout Issues menu on the bottom auto layout toolbar to select All View in View Controller \ Update Frames:
This repositions the label, and it becomes immediately obvious that you need to do some work on the appearance.
Select the multiline label, and use the Attributes Inspector to set the following:
- Title: Piece of Advice
- Alignment: Center
- Text Color: Keyboard Focus Indicator
- Font: System 20
Head over to the Size Inspector and set the Preferred Width to Explicit with a value of 75:
To get an idea of what the end product might look like, select the image view, and use the Attributes Inspector to set the Image to magic8ball:
Once again, click the main view and use the Resolve Auto Layout Issues\All Views in View Controller\Update Frames to update the layout:
This is looking pretty good. You need to respond each time the user clicks on the 8-ball—time to discover gesture recognizers.
Gesture Recognizers
You were able to create an action in code that would run every time the user clicks the button. That was possible because buttons are designed to handle clicks—but the same is not true of image views.
Apple has provided a set of gesture recognizers that can be added to any view. These recognizers convert low-level mouse and trackpad input into semantic events—such as click, zoom and rotate.
You will add a click gesture recognizer to the image view, and then create a corresponding action in the view controller’s code.
In Main.storyboard, head over to the Object Library and search for click gesture. Drag the Click Gesture Recognizer out of the library and onto the image view. It will appear like this in the document outline:
This gesture recognizer will be activated whenever the user clicks on the image—exactly what you want. You now need to create some new connections, and then you’ll be ready to switch over to code for the final sprint.
IB Connections
In the same way you did before, open up the assistant editor and ensure that it is showing ViewController.swift. Then create two new outlets by control-dragging from the storyboard to the code: one for the image view (call it ballImageView) and one for the multiline label (call it adviceLabel). This adds the following new outlets:
@IBOutlet weak var ballImageView: NSImageView! @IBOutlet weak var adviceLabel: NSTextField! |
You also need an action to wire up the newly-created gesture recognizer. Control-drag from the click gesture recognizer in the document outline, over to the code:
Change the Connection to Action and name it handleBallClick:
Click Connect and Xcode will add the following function definition to the ViewController
class:
@IBAction func handleBallClick(sender: AnyObject) { } |
You’ve now finished all the work in Interface Builder, so you can switch back to the standard editor, and open ViewController.swift.
Manipulating UI from code
When the user clicks on the 8-ball you want to switch between showing some advice, or showing the “8”. This means that the handleBallClick(_:)
will manipulate both the image view and the advice label.
Add the following code to handleBallClick(_:)
:
// 1: if(adviceLabel.hidden) { // 2: adviceLabel.hidden = false ballImageView.image = NSImage(named: "magic8ball") } else { // 3: adviceLabel.hidden = true ballImageView.image = NSImage(named: "8ball") } |
- Check whether the
adviceLabel
is currently visible.hidden
is a boolean property onNSView
(and henceNSTextField
) that allows you to specify whether the view should be visible or not. - If the advice label is currently hidden then show it, and change the image to the magic-side.
NSImage(named:)
loads the image from the asset catalog, and theimage
property onNSImageView
specifies the image to display. - Conversely, if the advice label is currently visible then hide it and switch the ball back to the “8” side.
Build and run and click the ball to see it switching between showing the “8” and the piece of advice. Pretty neat right? Notice how when you first start the app the advice is already showing? That’s not really what you want, but it’s a simple fix.
Initial Setup
When the app first starts you want to ensure that the advice label is hidden, and the 8-ball image is showing. View controllers have a perfect way of configuring this initial setup—in the form of viewDidLoad()
.
Once the view controller has finished loading all the view components from the storyboard, it calls viewDidLoad()
to give you a chance to perform some final configuration.
In ViewController.swift, find the viewDidLoad()
method and add the following body:
adviceLabel.hidden = true ballImageView.image = NSImage(named: "8ball") |
You’ll recognize this code from the click handler action—it just hides the advice label and sets the image to 8ball.
Build and run to check that you can’t see the advice from the outset.
Advice Generator
At the moment, no matter how many times you “shake” the ball, it always gives you the same advice. That’s not especially helpful. Time to add a bit of randomness.
Add the following code as a property inside ViewController
—just below the class definition line:
let adviceList = [ "Yes", "No", "Ray says 'do it!'", "Maybe", "Try again later", "How can I know?", "Totally", "Never", ] |
This is an array of strings the make up all the different options for advice that the ball can dispense.
Head to the very bottom of the file (not within the ViewController
class) and add the following extension:
extension Array { var randomElement: Element? { if count < 1 { return .None } let randomIndex = arc4random_uniform(UInt32(count)) return self[Int(randomIndex)] } } |
This adds a new property to the standard library’s Array
type that will return a random element. If the array is empty it returns nil
, otherwise it generates a random index using arc4random_uniform()
before returning the corresponding element.
Update the first branch (i.e. adviceLabel.hidden == true
) of the if
statement in handleBallClick(_:)
to match the following:
if let advice = adviceList.randomElement { adviceLabel.stringValue = advice adviceLabel.hidden = false ballImageView.image = NSImage(named: "magic8ball") } |
This attempts to get a random piece of advice to display, and if successful updates the stringValue
on adviceLabel
to show it.
Build and run, and click the 8-ball a few times to start benefiting from the ball’s wisdom:
Where to go from here
You can download the completed version of MagicEight here.
This Mac OS X development tutorial introductory series has given you a basic level of knowledge to get started with OS X apps–but there’s so much more to learn!
Apple has some great documentation covering all aspects of OS X development – head on over to https://developer.apple.com/library/mac/navigation/
You should also stick around on raywenderlich.com – we’ve got some awesome OS X tutorials lined up for the coming months!
If you have any comments or questions feel free to reach out on twitter, or in the comments section below!
The post Mac OS X Development Tutorial for Beginners Part 3: Your First OS X App appeared first on Ray Wenderlich.