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 } }