diff --git a/Calculator/Calculator.xcodeproj/project.pbxproj b/Calculator/Calculator.xcodeproj/project.pbxproj index 71f12d9..0124e2b 100644 --- a/Calculator/Calculator.xcodeproj/project.pbxproj +++ b/Calculator/Calculator.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 90A26ED9298BB29000A9C749 /* UIStackView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90A26ED8298BB29000A9C749 /* UIStackView+Extension.swift */; }; C713D9422570E5EB001C3AFC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C713D9412570E5EB001C3AFC /* AppDelegate.swift */; }; C713D9442570E5EB001C3AFC /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C713D9432570E5EB001C3AFC /* SceneDelegate.swift */; }; C713D9462570E5EB001C3AFC /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C713D9452570E5EB001C3AFC /* ViewController.swift */; }; @@ -35,6 +36,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 90A26ED8298BB29000A9C749 /* UIStackView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIStackView+Extension.swift"; sourceTree = ""; }; C713D93E2570E5EB001C3AFC /* Calculator.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Calculator.app; sourceTree = BUILT_PRODUCTS_DIR; }; C713D9412570E5EB001C3AFC /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; C713D9432570E5EB001C3AFC /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; @@ -105,6 +107,7 @@ D94FF8362974D9B100F7A551 /* CalculateItemQueue.swift */, D94FF84A29751BD500F7A551 /* CalculateItem.swift */, D94BC7B5297670DD00186B6A /* ExpressionParser.swift */, + 90A26ED8298BB29000A9C749 /* UIStackView+Extension.swift */, ); path = Model; sourceTree = ""; @@ -184,7 +187,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1420; - LastUpgradeCheck = 1210; + LastUpgradeCheck = 1420; TargetAttributes = { C713D93D2570E5EB001C3AFC = { CreatedOnToolsVersion = 12.1; @@ -239,6 +242,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 90A26ED9298BB29000A9C749 /* UIStackView+Extension.swift in Sources */, C713D9462570E5EB001C3AFC /* ViewController.swift in Sources */, D94BC7B6297670DD00186B6A /* ExpressionParser.swift in Sources */, C713D9422570E5EB001C3AFC /* AppDelegate.swift in Sources */, @@ -345,7 +349,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 14.1; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = NO; + ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; @@ -463,6 +467,7 @@ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = ""; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 16.2; @@ -488,6 +493,7 @@ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = ""; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 16.2; diff --git a/Calculator/Calculator/Controller/ViewController.swift b/Calculator/Calculator/Controller/ViewController.swift index 222c278..216eaa3 100644 --- a/Calculator/Calculator/Controller/ViewController.swift +++ b/Calculator/Calculator/Controller/ViewController.swift @@ -7,12 +7,173 @@ import UIKit class ViewController: UIViewController { + private let zero: String = "0" + private let point: String = "." + private var calculationFormula: String = "+" + private var isDecimal: Bool = false + private var isCalculated: Bool = false + private var enteredOperand: String = "0" override func viewDidLoad() { super.viewDidLoad() - // Do any additional setup after loading the view. } + @IBOutlet weak var scrollView: UIScrollView! + @IBOutlet weak var operatorLabel: UILabel! + @IBOutlet weak var operandLabel: UILabel! + @IBOutlet weak var formulaStackViews: UIStackView! -} + @IBAction func allClear(_ sender: UIButton) { + operandLabel.text?.removeAll() + isDecimal = false + operatorLabel.text = "" + calculationFormula = "+" + initializeOperand() + formulaStackViews.removeAllArrangedSubviews() + } + + @IBAction func clearEntry(_ sender: UIButton) { + guard isInitialOperand() == false, enteredOperand != String(Double.nan), isCalculated == false else { + return + } + if enteredOperand.removeLast() == Character(point) { + isDecimal = false + } + guard enteredOperand.isEmpty == false else { + initializeOperand() + return + } + operandLabel.text = formattingNumber(enteredOperand) + } + + @IBAction func changeSign(_ sender: UIButton) { + guard isInitialOperand() == false else { + return + } + if enteredOperand.first == "-" { + enteredOperand.removeFirst() + } else { + enteredOperand = "-" + enteredOperand + } + operandLabel.text = formattingNumber(enteredOperand) + } + + @IBAction func addDecimalPoint(_ sender: UIButton) { + guard isDecimal == false, isCalculated == false else { + return + } + enteredOperand += point + isDecimal = true + operandLabel.text = formattingNumber(enteredOperand) + point + } + + @IBAction func addOperand(_ sender: UIButton) { + guard let number = sender.currentTitle else { + return + } + if isCalculated { + enteredOperand = zero + isCalculated = false + } + guard isInitialOperand() == false || isZero(number) == false else { + return + } + if isInitialOperand() { + enteredOperand = number + } else { + enteredOperand += number + } + operandLabel.text = convertOperand(enteredOperand) + } + + @IBAction func addOperator(_ sender: UIButton) { + guard let `operator` = sender.currentTitle, + let enteredOperator = operatorLabel.text else { + return + } + guard isInitialOperand() == false else { + if calculationFormula == "+" { + calculationFormula.removeAll() + } + operatorLabel.text = `operator` + return + } + if enteredOperand.hasSuffix(point) { + enteredOperand += zero + } + calculationFormula += enteredOperator + enteredOperand + addFormulaStackView(operator: enteredOperator, operand: enteredOperand) + operatorLabel.text = `operator` + initializeOperand() + isDecimal = false + scrollView.contentOffset.y = scrollView.contentSize.height + } + + @IBAction func calculateResult(_ sender: UIButton) { + guard let enteredOperator = operatorLabel.text, calculationFormula != "+" else { + return + } + calculationFormula += enteredOperator + enteredOperand + operatorLabel.text = "" + isDecimal = false + isCalculated = true + var formula = ExpressionParser.parse(from: calculationFormula) + addFormulaStackView(operator: enteredOperator, operand: enteredOperand) + enteredOperand = String(formula.result()) + operandLabel.text = formattingNumber(enteredOperand) + calculationFormula = "+" + scrollView.contentOffset.y = scrollView.contentSize.height + } + private func addFormulaStackView(`operator`: String, operand: String) { + let formulaStackView = UIStackView() + let enteredOperatorLabel = UILabel() + let enteredOperandLabel = UILabel() + + enteredOperandLabel.text = formattingNumber(operand) + enteredOperatorLabel.text = `operator` + enteredOperandLabel.textColor = .white + enteredOperatorLabel.textColor = .white + formulaStackView.axis = .horizontal + formulaStackView.spacing = 8 + formulaStackView.addArrangedSubview(enteredOperatorLabel) + formulaStackView.addArrangedSubview(enteredOperandLabel) + + formulaStackViews.addArrangedSubview(formulaStackView) + } + + private func formattingNumber(_ number: String) -> String { + let formatter = NumberFormatter() + formatter.maximumFractionDigits = 20 + formatter.numberStyle = .decimal + return formatter.string(for: Double(number)) ?? number + } + + private func convertOperand(_ operand: String) -> String { + if let indexOfPoint = operand.firstIndex(of: Character(point)) { + let integerOfOperand = String(operand[operand.startIndex.. 20 { + let twentyIndex = operand.index(indexOfPoint, offsetBy: 20) + decimalOfOperand = String(operand[indexOfPoint...twentyIndex]) + } else { + decimalOfOperand = String(operand[indexOfPoint.. Bool { + return enteredOperand == zero + } + + private func isZero(_ value: String) -> Bool { + return value == zero || value == "00" + } + + private func initializeOperand() { + enteredOperand = zero + operandLabel.text = zero + } +} diff --git a/Calculator/Calculator/Model/CalculateItem.swift b/Calculator/Calculator/Model/CalculateItem.swift index 77ac123..c83565e 100644 --- a/Calculator/Calculator/Model/CalculateItem.swift +++ b/Calculator/Calculator/Model/CalculateItem.swift @@ -13,9 +13,9 @@ protocol CalculateItem { enum Operator: Character, CalculateItem, CaseIterable { case add = "+" - case subtract = "-" - case divide = "/" - case multiply = "*" + case subtract = "−" + case divide = "÷" + case multiply = "×" func calculate(lhs: Double, rhs: Double) -> Double { switch self { diff --git a/Calculator/Calculator/Model/UIStackView+Extension.swift b/Calculator/Calculator/Model/UIStackView+Extension.swift new file mode 100644 index 0000000..3e2571b --- /dev/null +++ b/Calculator/Calculator/Model/UIStackView+Extension.swift @@ -0,0 +1,17 @@ +// +// UIStackView+Extension.swift +// Calculator +// +// Created by 박재우 on 2023/02/02. +// + +import UIKit + +extension UIStackView { + func removeAllArrangedSubviews() { + arrangedSubviews.forEach { + self.removeArrangedSubview($0) + $0.removeFromSuperview() + } + } +} diff --git a/Calculator/Calculator/View/Base.lproj/Main.storyboard b/Calculator/Calculator/View/Base.lproj/Main.storyboard index 3d0be3e..d5172ef 100644 --- a/Calculator/Calculator/View/Base.lproj/Main.storyboard +++ b/Calculator/Calculator/View/Base.lproj/Main.storyboard @@ -1,8 +1,9 @@ - + - + + @@ -17,39 +18,16 @@ - + - + - - - - - - - - + - - @@ -121,6 +111,9 @@ + + + @@ -158,6 +160,9 @@ + + + @@ -195,6 +209,9 @@ + + + @@ -232,6 +258,9 @@ + + + - + - + -