Training and Instructional Design
[Note: Updated for Swift 2.0/iOS9 9/1/15 SJL]
Last time we built a small application to compute the area of a pizza. Since it was missing a model, we are going to make an improved version of the application and add a model class, plus make a few UI changes. Since there are so many changes, we’ll start from the beginning. If you did not read the last post in the series, you will be fine though you might want to catch up on the concepts discussed earlier here. I’ll assume you have at least Xcode 7, and have some clue how to set up a story board and view controller.
In Xcode, Create a new single-view Swift project. Call it PizzaDemo2. Go into the storyboard. On the bottom center of the storyboard, you will find the class sizes button labeled w:Any h:Any. Click the button and change the class size to Compact Width Any Height:
To the storyboard add a label, two buttons, and a segmented control. Make one of the buttons with a red background and white lettering, make the title of the button read Clear. Set the second button to a white foreground lettering and a blue background, but don’t set a title yet. Make the Segmented control four segments with Cheese, Sausage, Pepperoni,and Veggie. Your layout should look something like this
I did not use constraints here, but if you want to you certainly can.
Open the assistant editor, and control drag the controls to make the following IBOutlets
and IBActions
. note in the comments which one is the red and which one is the blue button. Remember from last time to physically change from an outlet to an action for the buttons and the segmented controls. Interface builder picks an outlet by default.
@IBOutlet var resultsDisplayLabel : UILabel! @IBAction func pizzaType(sender : UISegmentedControl) { } @IBAction func sizeButton(sender : UIButton) { //blue button } @IBAction func clearDisplayButton(sender : UIButton) { //red button }
In the last post in this series, we used a slider to specify pizza size. We will use preset buttons this time. We are going to use our blue sizeButton
to specify the size in its label. Go back into interface builder and click once on the blue button. Copy the button, then paste it 6 times. Change the titles and arrange the buttons to look like this:
In case you have never done this before, this is a way to have multiple buttons use the same IBAction
. The properties can be different for the buttons, in our case the titles, but they react exactly the same to being pressed. Our model will take the title string and convert it into a pizza size.
Between the import
and the class ViewController:
code, make a little space and add the following:
class Pizza { let pi = 3.1415926 let maxPizza = 24.0 var pizzaDiameter = 0.0 var pizzaType = "Cheese" }
With that small amount of code, we created a class with two properties and two constants. That’s it. Swift does require initialized values, thus we have to specify an initialization value. Implicit typing figures out what type to use by our initialized values. When we declare a variable in a class in Swift, it becomes a property of that class. Similarly declaring a constant becomes a constant of the class.
Adding methods is also easy. We did it last time in the view controller. A function within a class becomes a method of that class.
Add the following under var pizzaType = "Cheese"
func pizzaArea() -> Double{ return radius * radius * pi }
We have not had a case yet where we return a value until now. The pizzaArea()
method returns a double. In Objective-C, and many C syntax languages we have the return value first, something like -(Double)pizzaArea{}
In Swift, the return value is on the end with an operator ->
to specify the type of the return value. if there is no return value, it is the equivalent of (void)
in C-syntax languages. In this case we return the area.
We are getting a syntax error now since radius is not defined. We’ll come back to radius shortly and show one of the really cool features of Swift.
Swift made a few changes to the switch
control Add this method under pizzaArea():
func diameterFromString(aString:String) -> Double { switch aString { case "Personal": return 8.0 case "10\"": return 10.0 case "12\"": return 12.0 case "16\"","15\"": return 16.0 case "18\"": return 18.0 case "24\"": return 24.0 default: return 0.0 } }
Switch in Swift does not use breaks. We can also list several cases for each case, so a 15″ and a 16″ pizza returns 16. The Swift switch
does need all cases be covered, so the default:
case is mandatory to catch everything not stipulated above the default. In our example, this will send 0.0 when we press the None button since it is not a specified size.
This method also has a parameter. We specify the variable name and then the type, separated by a colon for parameters.
We we still need the radius. We could make a method to do this, but there is another way. Add the following code between the pizzaArea()
and the var pizzaType="Cheese"
declaration.
var radius : Double { //computed property get{ //must define a getter return pizzaDiameter/2.0 } set(newRadius){ //optionally define a setter pizzaDiameter = newRadius * 2.0 } } var area : Double { get{ return pizzaArea() } }
Line 1 and line 10 declares a computed property, a property based on the values of other properties. Computed properties must declare their type, and must include a getter. Within the block after the declaration, we declare a getter named get
which returns a value computed by the already declared property pizzaDiameter
. Optionally you can include a setter for the property, as we did in line 5 for the radius. In our second example, we made a second computed property with only a getter for the area, which uses a method in our class.
We can now use our model in our code. Before we do let’s write a handler for the clear button. Change clearDisplayButton
to this:
@IBAction func clearDisplayButton(sender : UIButton) { resultsDisplayLabel.text = clearString }
For our initial setup change viewDidLoad
to this:
override func viewDidLoad() { super.viewDidLoad() resultsDisplayLabel.text = clearString view.backgroundColor = UIColor(red:0.99,green:0.9,blue:0.9,alpha:1.0) }
Just under the view controller’s class declaration add the following:
let pizza = Pizza() let clearString = "I Like Pizza!"
Line 2 is a simple string constant declaration used for clearing the label. Line 1 is all we need to instantiate a pizza object: declare the object as a constant and give a class for the object. Even though pizza
is a constant, its properties are not constants. This works, and keeps better memory allocation than using var
. Now add a display method for the label:
func displayPizza(){ let displayString = String(format:"%6.1f inch %@ Pizza",pizza.pizzaDiameter, pizza.pizzaType) resultsDisplayLabel.text = displayString }
In line 2, we use dot notation to use the properties of the pizza
instance. We do the same for methods as well. Add the following code for the size button:
@IBAction func sizeButton(sender : UIButton) { pizza.pizzaDiameter = pizza.diameterFromString(sender.titleLabel!.text!) displayPizza() }
The size button reads the title of the button called sender
, and assigns the correct size for a pizza to pizza
. Line 2 calls in pizza
the method diameterfromString()
through dot notation.
Now add the handler for selecting a pizzaType:
@IBAction func pizzaType(sender : UISegmentedControl) { let index = sender.selectedSegmentIndex pizza.pizzaType = sender.titleForSegmentAtIndex(index)! displayPizza() }
Line 2 gets the index of the segment selected. Line 3 get a string with the type of pizza from the segement’s label. You’ll notice the !
after the function here and in line 2 of the sizeButton
code. This is a forced unwrap operator. For this lesson, just put them in – I’ll explain them in the next lesson
We have gotten everything into place, and can now build and run.
We didn’t display anything for the area like we did in the first computer. Next time we will add some more functionality by using some collection types to our pizza computer to sell pizza by the square inch so we can calculate quickly how much to sell a pizza for.
I should have made this two files: one for the model and one for the controller. However, since Swift lets me put them in one file, I did that to make copying and pasting a bit easier.
// // ViewController.swift // pizzaDemo version 2 // adds a model class to demonstrate class // // Created by Steven Lipton on 6/8/14. // Revised by Steven Lipton on 9/1/15 for Swift 2.0 (Beta 4 tested) // Copyright (c) 2014 Steven Lipton. All rights reserved. // // import UIKit /* -------- Our model for MVC keeps data and calculations about pizzas note: for ease in copying I left this in one file you can make a separate file and use import instead. ------------*/ class Pizza { let pi = 3.1415926 let maxPizza = 24.0 var pizzaDiameter = 0.0 var pizzaType = "Cheese" var radius : Double { //computed property get{ //must define a getter return pizzaDiameter/2.0 } set(newRadius){ //optionally define a setter pizzaDiameter = newRadius * 2.0 } } var area : Double { get{ return pizzaArea() } } func pizzaArea() -> Double{ return radius * radius * pi } func diameterFromString(aString:String) -> Double { switch aString { case "Personal": return 8.0 case "10\"": return 10.0 case "12\"": return 12.0 case "16\"","15\"": return 16.0 case "18\"": return 18.0 case "24\"": return 24.0 default: return 0.0 } } } /*---------- The View Controller -----------------*/ class ViewController: UIViewController { let pizza = Pizza() let clearString = "I Like Pizza!" @IBOutlet var resultsDisplayLabel : UILabel! func displayPizza(){ let displayString = String(format:"%6.1f inch %@ Pizza",pizza.pizzaDiameter, pizza.pizzaType) resultsDisplayLabel.text = displayString } @IBAction func pizzaType(sender : UISegmentedControl) { let index = sender.selectedSegmentIndex pizza.pizzaType = sender.titleForSegmentAtIndex(index)! displayPizza() } @IBAction func sizeButton(sender : UIButton) { pizza.pizzaDiameter = pizza.diameterFromString(sender.titleLabel!.text!) displayPizza() } @IBAction func clearDisplayButton(sender : UIButton) { resultsDisplayLabel.text = clearString } override func viewDidLoad() { super.viewDidLoad() resultsDisplayLabel.text = clearString view.backgroundColor = UIColor(red:0.99,green:0.9,blue:0.9,alpha:1.0) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } }
Pingback: Using Delegates and Segues part 2 — The Pizza Demo App | Making App Pie
Thanks for the tutorial. Just one thing to point out in case other’s have the same problem, I couldn’t get sender.titleLabel.text to work. After a lot of googling I found a solution like this:
if let text = sender.textLabel.text {
pizza.pizzaDiameter = pizza.diameterFromString(text)
}
I’m new to iPhone development and especially swift, so if this is wrong, let me know. But at least it got me past this part to move on the the rest of the tutorial.
It’s not wrong — good catch and good job at research to fix it. Welcome to the joys of Beta Swift. Apple has been changing a lot of the API to standardize it for Swift, and one thing they have done particularly since beta 7 and the GM seed is change many of the implicit optionals to explicit. What you wrote is one way of getting the right answer, though a bit verbose. It does have a few places where it’s very useful — it’s the only way to get a
UISplitViewController
to work.Both the
titlelabel
andtext
are optionals, so both need to be forced into value. The code you found is one of the gentle ways of doing that. The one I use is the short but brutal way.Check out my tutorial on optionals Ten Points for Using Optionals. to understand the difference between the two and My post Using Dictionaries and Optional Types for an introduction to these pesky things called optionals. I also posted a longer explanation as my latest blog post
Enjoy your journey into the world of iPhone development and Swift.
Pingback: The Joys of Beta Swift: More with Optionals and the ” does not have a member named ” error | Making App Pie
Pingback: Swift WatchKit Tutorials — A Basic Watch Kit App | Making App Pie
It is nicely explain…I am facing the problem with the Lay-out of the Pizza size buttons which are in one row. In the Simulator some buttons going out of the frame..I tried several constrains and also tried UIView but no help…Can you demonstrate please…Thanks
Probably it is the size classes that’s the problem. if you look at the bottom of the storybaord pane, you should see w:Any h:Any. Click it and change the size class to this:

Clear out all your constraints and you should be fine.
I added that to the tutorial as well. Thank you for finding that error.
Thanks for the answer….appreciated. It is working.
Just for the suggestion, It would be very great if you come with one tutorial with all aspects of Auto-Layout and Constrains in swift with different kind of cases of layout, as I was searching several resources to solve this Issue, I came across several aspects of Auto layout and constrains…several sources describe basic knowledge on outlet, but very few sources described the real practical cases like in above code….Thanks
For autolayout information you can check out my book Practical Autolayout available from Amazon for Kindle at http://www.amazon.com/dp/B010BL1WSG
Pingback: The Swift Swift Tutorial: Using Dictionaries and Optional Types | Making App Pie
Pingback: How MakeAppPie.com is Updating to Swift 2.0 | Making App Pie
Pingback: How To Make Your First WatchOS2 App | Making App Pie