This is part 5 of my Learning Swift Calculator App.
This update is mostly about the code structure and design decision about the calculator and its behavior.
Technical lessons learned:
- Adding variable values in the middle of the string is so much easier using the following format – “blah blah blah and I am going to add /(variable) in the middle of it” Â Assuming that variable:String = “some words” then the previous string becomes “blah blah blah and I am going to add some words in the middle of it”
- Be careful when modifying IBOutlets. Â Renaming it in Viewcontroller.swift does not modify the IBOutlet link (in the Storyboard). Â You have to relink it. Â The error given by xCode is not particular helpful either.
Design lesson learned:
- Contextual buttons would make the code so much cleaner, but calculators have been around so long that people expect certain behaviors.
- An example of what I mean by contextual button is this: after pressing = it would make it much clearer to the user what the calculator is actually doing if the equal button disappeared. Â However, many of us have come to expect the equal button to mean, repeat last operation. Â So, if you press 1+1= (the calculator shows 2), then if you press = again, the calculator will show 3.
After spending a few hours banging my head against some weird behavior, I started from scratch with a state diagram.
The general gist is as follows:

This is generally being modeled after the OS X calculator. Â The tricky part is that the operators (+,-,x, etc.) do different things depending on what the user input was previous to pressing the operator buttons. Â If a user presses + after a calculation already took place, the calculator is essentially doing a = and then + presenting the user with the calculated value, then getting ready to accept the next input value. Â This is completely different from my original design, where I took a string and processed the calculation when the user pressed =.
I’ve already spent a few hours on this logic and it’s much more complicated than I thought.
Here’s the code so far. Â Still very buggy.
//
// ViewController.swift
// Calculator
//
// Created by Jin S. An on 10/1/14.
// Copyright (c) 2014 jinsungpsu.com. All rights reserved.
//
import UIKit
class ViewController: UIViewController {
@IBOutlet var calcDisplay1: UITextField!
@IBOutlet var calcDisplayOp: UITextField!
var clearDisplay:Bool = false
var firstOp:Bool = true
var num1:Float = 0.0
var num2:Float = 0.0
@IBOutlet var btn01: UIButton!
@IBOutlet var btn02: UIButton!
@IBOutlet var btn03: UIButton!
@IBOutlet var btn04: UIButton!
@IBOutlet var btn05: UIButton!
@IBOutlet var btn06: UIButton!
@IBOutlet var btn07: UIButton!
@IBOutlet var btn08: UIButton!
@IBOutlet var btn09: UIButton!
@IBOutlet var btnDecimal: UIButton!
@IBOutlet var btn00: UIButton!
@IBOutlet var btnEqual: UIButton!
@IBOutlet var btnClear: UIButton!
@IBOutlet var btnPlus: UIButton!
@IBOutlet var btnMinus: UIButton!
@IBOutlet var btnMultiply: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
btn01.showsTouchWhenHighlighted = true;
btn02.showsTouchWhenHighlighted = true;
btn03.showsTouchWhenHighlighted = true;
btn04.showsTouchWhenHighlighted = true;
btn05.showsTouchWhenHighlighted = true;
btn06.showsTouchWhenHighlighted = true;
btn07.showsTouchWhenHighlighted = true;
btn08.showsTouchWhenHighlighted = true;
btn09.showsTouchWhenHighlighted = true;
btn00.showsTouchWhenHighlighted = true;
btnDecimal.showsTouchWhenHighlighted = true;
btnEqual.showsTouchWhenHighlighted = true;
btnClear.showsTouchWhenHighlighted = true;
btnPlus.showsTouchWhenHighlighted = true;
btnMinus.showsTouchWhenHighlighted = true;
btnMultiply.showsTouchWhenHighlighted = true;
self.view.backgroundColor = UIColor.lightGrayColor()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func digitPressed(buttonPressed: String) {
if (calcDisplayOp.text != "") {
firstOp = false
}
if (clearDisplay == true){
calcDisplay1.text = ""
clearDisplay = false
}
calcDisplay1.text = (calcDisplay1.text as NSString)+buttonPressed
}
func calculateValue(op: String, n1: Float, n2: Float) -> Float{
var calculatedValue:Float = 0.0
switch (op) {
case "+":
calculatedValue = n1 + n2
case "-":
calculatedValue = n1 - n2
case "*":
calculatedValue = n1 * n2
default:
calculatedValue = -9999.9999
}
return calculatedValue
}
func operatorPressed(buttonPressed: String) {
calcDisplayOp.text = buttonPressed
if (calcDisplay1.text == "") { // if user press op before any digits, then assume that num1 = 0
} else { // this means that there is a number on display that the next operation needs to do something against.
if (firstOp == true) { // this is the first operation... don't present a calculated value until 2nd input from user
num1 = (calcDisplay1.text as NSString).floatValue
} else { //
num2 = (calcDisplay1.text as NSString).floatValue
calcDisplay1.text = "\(calculateValue(buttonPressed, n1: num1, n2: num2))"
num1 = num2
}
}
clearDisplay = true
/* CASES
EXPECTED CASE:
THERE IS A NUMBER ON THE DISPLAY, OPERATOR
ERROR CASES:
NOTHING ON THE DISPLAY, USER PRESSES AN OPERATION (OS X CALCULATOR ASSUMES FIRST NUMBER IS 0)
AMBIGUOUS CASES:
USER PRESSES AN OPERATOR FOLLOWING AN OPERATOR (OS X CALCULATOR OVERRIDES OPERATOR WITH LATEST PRESSED)
*/
}
@IBAction func calcEqual(sender: AnyObject) {
var currOp:String = calcDisplayOp.text
calcDisplayOp.text = "="
num2 = (calcDisplay1.text as NSString).floatValue
calcDisplay1.text = "\(calculateValue(currOp, n1: num1, n2: num2))"
num1 = num2
firstOp = true
clearDisplay = true
}
@IBAction func calc01(sender: AnyObject) {
digitPressed("1")
}
@IBAction func calc02(sender: AnyObject) {
digitPressed("2")
}
@IBAction func calc03(sender: AnyObject) {
digitPressed("3")
}
@IBAction func calc04(sender: AnyObject) {
digitPressed("4")
}
@IBAction func calc05(sender: AnyObject) {
digitPressed("5")
}
@IBAction func calc06(sender: AnyObject) {
digitPressed("6")
}
@IBAction func calc07(sender: AnyObject) {
digitPressed("7")
}
@IBAction func calc08(sender: AnyObject) {
digitPressed("8")
}
@IBAction func calc09(sender: AnyObject) {
digitPressed("9")
}
@IBAction func calcDecimal(sender: AnyObject) {
digitPressed(".")
}
@IBAction func calc00(sender: AnyObject) {
digitPressed("0")
}
@IBAction func calcPlus(sender: AnyObject) {
operatorPressed("+")
}
@IBAction func calcMinus(sender: AnyObject) {
operatorPressed("-")
}
@IBAction func calcMultiply(sender: AnyObject) {
operatorPressed("x")
}
@IBAction func calcClear(sender: AnyObject) {
calcDisplayOp.text = ""
calcDisplay1.text = ""
calcDisplayOp.text = ""
num1 = 0;
num2 = 0;
firstOp = true
clearDisplay = false
}
}