This is the first app I have ever built, and it feels good.
CalculatorViewController.swift
import UIKit
class CalculatorViewController: UIViewController {
@IBOutlet weak var decimalButton: UIButton!
@IBOutlet weak var subtractButton: UIButton!
@IBOutlet weak var divideButton: UIButton!
@IBOutlet weak var addButton: UIButton!
@IBOutlet weak var multiplyButton: UIButton!
@IBOutlet weak var equalsButton: UIButton!
@IBOutlet weak var outputLabel: UILabel!
@IBOutlet weak var clearButton: UIButton!
@IBOutlet weak var previousAnswerLabel: UILabel!
@IBOutlet weak var previousAnswerTitle: UILabel!
@IBOutlet weak var copyButton: UIButton!
var brain: CalculatorBrain?
let xYValue = 0.75
let durationTime = 0.15
override func viewDidLoad() {
super.viewDidLoad()
previousAnswerLabel.text = nil
previousAnswerTitle.text = nil
brain = CalculatorBrain()
}
// MARK: - Action Handlers
@IBAction func operandTapped(_ sender: UIButton) {
UIButton.animate(withDuration: durationTime,
animations: {
sender.transform = CGAffineTransform(scaleX: CGFloat(self.xYValue), y: CGFloat(self.xYValue))
},
completion: { finish in
UIButton.animate(withDuration: self.durationTime, animations: {
sender.transform = CGAffineTransform.identity
})
})
// Takes in the value of the operand tapped
if let operandTapped = sender.titleLabel?.text {
let buttonDoesntContainDecimal = !operandTapped.contains(".")
let buttonContainsDecimalWithNoCurrentDecimal = operandTapped.contains(".") && !(outputLabel.text?.contains(".") ?? false)
if buttonDoesntContainDecimal || buttonContainsDecimalWithNoCurrentDecimal {
outputLabel.text = brain?.addOperandDigit(operandTapped)
}
}
}
@IBAction func operatorTapped(_ sender: UIButton) {
UIButton.animate(withDuration: durationTime,
animations: { sender.transform = CGAffineTransform(scaleX: CGFloat(self.xYValue), y: CGFloat(self.xYValue))},
completion: { finish in
UIButton.animate(withDuration: self.durationTime, animations: {
sender.transform = CGAffineTransform.identity
})
})
// Takes in the value of the operator tapped
if let operatorValueTapped = sender.titleLabel?.text {
brain?.setOperator(operatorValueTapped)
}
}
@IBAction func equalTapped(_ sender: UIButton) {
UIButton.animate(withDuration: durationTime,
animations: {
sender.transform = CGAffineTransform(scaleX: CGFloat(self.xYValue), y: CGFloat(self.xYValue))
},
completion: { finish in
UIButton.animate(withDuration: self.durationTime, animations: {
sender.transform = CGAffineTransform.identity
})
})
// Calculates the answer and sets it to the value 'answer'
if let answer = brain!.calculateIfPossible() {
copyButton.setTitleColor(.green, for: .normal)
previousAnswerLabel.text = answer
previousAnswerTitle.text = "Previous Answer"
outputLabel.text = answer
}
}
@IBAction func clearTapped(_ sender: UIButton) {
UIButton.animate(withDuration: durationTime,
animations: {
sender.transform = CGAffineTransform(scaleX: CGFloat(self.xYValue), y: CGFloat(self.xYValue))
},
completion: { finish in
UIButton.animate(withDuration: self.durationTime, animations: {
sender.transform = CGAffineTransform.identity
})
})
// Clears the calculation when tapped
if outputLabel.text == "0" {
clearTransaction()
copyButton.setTitleColor(.black, for: .normal)
previousAnswerTitle.text = nil
previousAnswerLabel.text = nil
}
clearTransaction()
outputLabel.text = "0"
}
// MARK: - Private
private func clearTransaction() {
// Clear calculation function
brain = CalculatorBrain()
}
@IBAction func posNegButtonTapped(_ sender: UIButton) {
UIButton.animate(withDuration: durationTime,
animations: {
sender.transform = CGAffineTransform(scaleX: CGFloat(self.xYValue), y: CGFloat(self.xYValue))
},
completion: { finish in
UIButton.animate(withDuration: self.durationTime, animations: {
sender.transform = CGAffineTransform.identity
})
})
// Turns number negative or positive
if var posNegNumber = Double(outputLabel.text ?? "") {
posNegNumber = posNegNumber * -1.0
outputLabel.text = String(posNegNumber)
brain?.operand1String = String(posNegNumber)
}
}
@IBAction func percentButtonTapped(_ sender: UIButton) {
UIButton.animate(withDuration: durationTime,
animations: {
sender.transform = CGAffineTransform(scaleX: CGFloat(self.xYValue), y: CGFloat(self.xYValue))
},
completion: { finish in
UIButton.animate(withDuration: self.durationTime, animations: {
sender.transform = CGAffineTransform.identity
})
})
// Divides by 100.0 (Double) to return the percentageNumber
if var percentageNumber = Double(outputLabel.text ?? "") {
percentageNumber = percentageNumber / 100.0
outputLabel.text = String(percentageNumber)
brain?.operand1String = String(percentageNumber)
}
}
@IBAction func subtractButtonPressed(_ sender: Any) {
// Set color for button pressed
subtractButton.backgroundColor = .white
subtractButton.setTitleColor(.black, for: .normal)
// Set default colors back
divideButton.backgroundColor = .green
divideButton.setTitleColor(.white, for: .normal)
multiplyButton.backgroundColor = .green
multiplyButton.setTitleColor(.white, for: .normal)
addButton.backgroundColor = .green
addButton.setTitleColor(.white, for: .normal)
equalsButton.backgroundColor = .green
equalsButton.setTitleColor(.white, for: .normal)
}
@IBAction func divideButtonPressed(_ sender: Any) {
// Set color for button pressed
divideButton.backgroundColor = .white
divideButton.setTitleColor(.black, for: .normal)
// Set default colors back
subtractButton.backgroundColor = .green
subtractButton.setTitleColor(.white, for: .normal)
multiplyButton.backgroundColor = .green
multiplyButton.setTitleColor(.white, for: .normal)
addButton.backgroundColor = .green
addButton.setTitleColor(.white, for: .normal)
equalsButton.backgroundColor = .green
equalsButton.setTitleColor(.white, for: .normal)
}
@IBAction func multiplyButtonPressed(_ sender: Any) {
// Set color for button pressed
multiplyButton.backgroundColor = .white
multiplyButton.setTitleColor(.black, for: .normal)
// Set default colors back
subtractButton.backgroundColor = .green
subtractButton.setTitleColor(.white, for: .normal)
divideButton.backgroundColor = .green
divideButton.setTitleColor(.white, for: .normal)
addButton.backgroundColor = .green
addButton.setTitleColor(.white, for: .normal)
equalsButton.backgroundColor = .green
equalsButton.setTitleColor(.white, for: .normal)
}
@IBAction func addButtonPressed(_ sender: Any) {
// Set color for button pressed
addButton.backgroundColor = .white
addButton.setTitleColor(.black, for: .normal)
// Set default colors back
subtractButton.backgroundColor = .green
subtractButton.setTitleColor(.white, for: .normal)
multiplyButton.backgroundColor = .green
multiplyButton.setTitleColor(.white, for: .normal)
divideButton.backgroundColor = .green
divideButton.setTitleColor(.white, for: .normal)
equalsButton.backgroundColor = .green
equalsButton.setTitleColor(.white, for: .normal)
}
@IBAction func equalButtonPressed(_ sender: Any) {
// Set color for button pressed
equalsButton.backgroundColor = .white
equalsButton.setTitleColor(.black, for: .normal)
// Set default colors back
subtractButton.backgroundColor = .green
subtractButton.setTitleColor(.white, for: .normal)
multiplyButton.backgroundColor = .green
multiplyButton.setTitleColor(.white, for: .normal)
addButton.backgroundColor = .green
addButton.setTitleColor(.white, for: .normal)
divideButton.backgroundColor = .green
divideButton.setTitleColor(.white, for: .normal)
}
@IBAction func clearButtonPressed(_ sender: Any) {
// Default all colors when clear button pressed
equalsButton.backgroundColor = .green
equalsButton.setTitleColor(.white, for: .normal)
subtractButton.backgroundColor = .green
subtractButton.setTitleColor(.white, for: .normal)
multiplyButton.backgroundColor = .green
multiplyButton.setTitleColor(.white, for: .normal)
addButton.backgroundColor = .green
addButton.setTitleColor(.white, for: .normal)
divideButton.backgroundColor = .green
divideButton.setTitleColor(.white, for: .normal)
}
@IBAction func copyButtonPressed(_ sender: Any) {
outputLabel.text = previousAnswerLabel.text
brain?.operand1String = previousAnswerLabel.text ?? ""
}
}
CalculatorBrain.swift
import Foundation
enum OperatorType: String {
case addition = "+"
case subtraction = "−"
case multiplication = "×"
case division = "÷"
}
class CalculatorBrain {
var operand1String = ""
var operand2String = ""
var operatorType: OperatorType?
func addOperandDigit(_ digit: String) -> String {
// Adds operand digit if nil
if operatorType != nil {
operand2String.append(digit)
return operand2String
} else {
operand1String.append(digit)
return operand1String
}
}
func setOperator(_ operatorString: String) {
// Sets the operator to it's rawValue
operatorType = OperatorType(rawValue: operatorString)
}
func calculateIfPossible() -> String? {
// Calculation for .addition, .subtraction, .multiplication, . division
// Checking to see if oprand1String or operand2String are empty
let numberFormatting = NumberFormatter()
numberFormatting.usesGroupingSeparator = true
numberFormatting.numberStyle = .decimal
numberFormatting.locale = Locale.current
if operand1String.isEmpty, operand2String.isEmpty, operatorType == nil {
return "0"
} else {
var finalAnswer: String
if let firstOperandNumber = Double(operand1String), let secondOperandNumber = Double(operand2String) {
switch operatorType {
case .addition? :
finalAnswer = String(firstOperandNumber + secondOperandNumber)
case .subtraction? :
finalAnswer = String(firstOperandNumber - secondOperandNumber)
case .multiplication? :
finalAnswer = String(firstOperandNumber * secondOperandNumber)
case .division? :
if secondOperandNumber == 0 {
return "Nice try ;)"
} else {
finalAnswer = String(firstOperandNumber / secondOperandNumber)
}
default :
return nil
}
} else {
return nil
}
operand1String = finalAnswer
operand2String = ""
return finalAnswer
}
}
}
Top comments (1)
Congratulations ! 👏
Take care to separate your code, one part for UI and one part for data otherwise you will have a Massive View Controller.