[Swift] 나만의 계산기 만들기(1) : Xcode 다루기, 스토리보드, IBAction

Oni·2023년 9월 1일
0

TIL

목록 보기
8/47
post-thumbnail

원문 포스팅 🔗

오늘은 Swift 기본 문법 강의를 기반으로 나만의 계산기 만들기 도전!

필수 구현 기능

1) 사칙연산 클래스 생성 및 클래스 활용 연산 수행
2) 나머지 연산 기능 추가
3) +, -, *, / 별도 클래스 생성 후 사칙연산 클랙스와 관계 맺기
3-1) 사칙연산 클래스 내부코드 변경
3-2) 2)번 항목과 달라진 점(단일책임원칙에 포커스를 둬서

필수 구현 기능 중에서 하루종일 시도 끝에 1번 완료

1. Xcode로 프로젝트 만들기

이때까지 Playgrounds로만 실습해왔는데 계산기 어플을 만들기 위해서는 Xcode 프로젝트를 생성해야 한다.
iOS 앱으로 계산기를 만들거니까 iOS APP 프로젝트를 선택한다.

아 혹시나 맥북 창 캡쳐 단축키 모르시면 command + 4 + 스페이스바 누르고 캡쳐할 창 선택하면 됩니다.

2. Xcode 살펴보기

Xcode 새 프로젝트를 생성하면 보이는 화면은 Main(storyboard) 이다.
Main 화면은 스토리보드 형태로 어플 작동 화면의 개략적인 구조와 경로등을 설정할 수 있고, 버튼이나 박스 등 이미지를 쉽게 만들 수 있다.


새 Swift 파일을 추가하는건 간단하다.
파일을 추가할 폴더를 우클릭해서 New File 클릭 후 만들고자하는 파일형태를 선택해서 만들면 된다.
나는 클래스를 따로 구분하기 위해서 Calculator 라는 파일을 생성했다.



3. Main.storyboard 계산기 그리기

버튼을 그리는건 아주 간단하다.
상단 우측에 있는 + 버튼을 클릭하면 어떤 요소를 추가할지 선택할 수 있다.
나는 두번째 Button을 선택해서 숫자와 연산자를 그렸고, 디스플레이 구현을 위해서 Label을 선택했다.

그리고 중요한건 버튼을 클릭했을 때 어떤 코드를 구현할 것인지 액션 메서드를 설정해야 하는데 어떻게 설정하는지 몰라서 검색을 했는데 잘 안나오는거다.
그래서 이 부분에서 많이 애먹었는데, 진짜 별거 아니라서 안나오는거더라...
버튼을 우클릭하거나 생성한 액션 버튼(@IBAction func)에 마우스를 갖다대면 왼쪽에 비어있는 동그라미가 뜰거다.
마우스로 클릭 후 드래그 앤 드롭 형태로 코드나 버튼에 가져다 끌면 추가되는 것을 볼 수 있다.
진짜 별거 아닌데 모르니까 시간을 금방 잡아 먹더라. (혹시라도 저와 같은 분이 계시다면 참조하시길,,)

지금은 기능부터 구현하려고 UI에는 크게 신경을 못썼고, 배치만 아이폰 내장 계산기를 참조하여 작업했다.

4. 계산기 기능 구현

4-1. 숫자 버튼 디스플레이에 나타내기

숫자뿐만아니라 소수점(.)도 디스플레이에 구현하기 위해서 11개 버튼에 액션 메서드를 추가했다.

버튼 클릭 동작을 touch 라고 이름을 지었으며, 처음 숫자를 누를 때 0인 경우에는 append 되지 않게 조건을 설정하였다.
(digit 를 문자열로 정의했기 때문에 0을 두번누르면 00으로 인식해서 디스플레이에 007650 형태의 숫자가 뜨는 오류가 있었다.)

&& : 논리연산자 AND

if digit == "0" && display.text == "0" {
    return
}
그리고 소수점의 경우에도 첫번째만 인식하고 나머지의 경우를 무시하기 위해 아래 코드를 추가하였다.

```swift
if digit == "." {
	if let text = display.text, text.contains(".") {
		return
	}
}

디스플레이에 0이 있으면 입력한 숫자로 나타내게 하였으며, 그 외의 경우 문자열을 나열하는 형태로 작성했다.

    if touchDigit {
        if display.text == "0" {
            display.text = digit
        } else {
            display.text?.append(digit)
        }
    } else {
        display.text = digit
        touchDigit = true
    }

4-2. 연산자

처음 클릭한 숫자를 number로 인식하고 클릭한 연산자를 symbol로 인식한다

@IBAction func operatorTouched(_ sender: UIButton) {
    guard let symbol = sender.currentTitle else { return }
    print("Touched operator: \(symbol)")

    if let number = Double(display.text!) {
        firstNumber = number
        operatorSymbol = symbol
        touchDigit = false
    }
}

4-3. 사칙연산 클래스로 구현하고 결과값 보여주기

디스플레이에 보여지는 문자열을 Double 형태로 바꿔준 값을 number로 정의하고, Calculator 클래스를 이용하여 계산값을 보여주는 코드이다.

@IBAction func resultTouched(_ sender: UIButton) {
    if let number = Double(display.text!),
       let symbol = operatorSymbol,
       let result = calculator.calculate(
            firstNumber: firstNumber, 
            operatorSymbol: symbol, 
            secondNumber: number) {
        display.text = String(result)
        firstNumber = result
        touchDigit = false
    }
}

※ 주의사항
현재 ViewController와 Calculator는 다른 Swift 파일로 작성되어 있어
Calculator 클래스를 사용하기 위해서는 ViewController 파일 상단에 아래 코드를 추가해야 한다.

var calculator: Calculator = Calculator()

Calculator 클래스는 아래와 같이 조건문(switch)으로 작성하였다.

import Foundation

class Calculator {
    
    func calculate(
    	firstNumber: Double?, operatorSymbol: String, secondNumber: Double) -> Double? {
        guard let fn = firstNumber else {
            return nil
        }
        
    switch operatorSymbol {
    case "+":
        return fn + secondNumber
    case "-":
        return fn - secondNumber
    case "×":
        return fn * secondNumber
    case "÷":
        if secondNumber != 0 {
            return fn / secondNumber
        } else {
            print("ERROR: Division by zero")
            return nil
        }
    default:
        print("ERROR")
        return nil
        
        }
        
    }
    
}

나누기의 경우 조건문을 두어 0으로 나누는 값을 ERROR로 프린트하게끔 설정하였다.
항상 nil 값을 리턴할 수 있게 신경써야 한다.(이것 때문에 에러 꽤 많이 나온..)

4-4. %, +/- 기능 구현하기

퍼센트(%)의 경우 0인 값을 제외해야 하므로 상기 썼던 코드 재활용해주고 입력 숫자값에 0.01을 곱해서 퍼센트를 표현해준다.
입력한 값은 문자열로 인식되므로 Double 형태로 변환하여 연산(곱셈)을 해주어야 한다.

@IBAction func percent(_ sender: UIButton) {
    guard let digit = sender.currentTitle else { return }

    if digit == "0" && display.text == "0" {
        return
    }

    if touchDigit {
        if display.text == "0" {
            display.text = digit
        } else if let currentNumber = Double(display.text ?? "") {
            let percentValue = currentNumber * 0.01
            display.text = String(percentValue)
        }
    } else {
        touchDigit = false
    }
}

플러스마이너스(+/-)의 경우에도 마찬가지로 0 값을 제외하는 코드를 작성하고 이번에는 -1을 곱해준다.

@IBAction func plusMinus(_ sender: UIButton) {
    guard let digit = sender.currentTitle else { return }

    if digit == "0" && display.text == "0" {
        return
    }

    if touchDigit {
        if display.text == "0" {
            display.text = digit
        } else if let currentNumber = Double(display.text ?? "") {
            let plusMinusValue = currentNumber * -1
            display.text = String(plusMinusValue)
        }
    } else {
        touchDigit = false
    }
}

퍼센트(%)와 플러스마이너스(+/-)는 어떤 값을 곱하냐만 차이가 날 뿐이지 똑같은 형태의 코드를 가진다.

4-6. clear 모든 값 초기화 하기

입력된 모든 값을 초기화하는 기능으로 디스플레이, 입력 값, 연산자 모두 초기화한다.

@IBAction func clear(_ sender: UIButton) {
    display.text = "0"
    firstNumber = nil
    operatorSymbol = nil
    touchDigit = false
}

5. 완성코드

ViewController.swift

import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var display: UILabel!

    var firstNumber: Double?
    var operatorSymbol: String?
    var touchDigit = false
    var calculator: Calculator = Calculator()

    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    @IBAction func touch(_ sender: UIButton) {
        guard let digit = sender.currentTitle else { return }
        print("Touched \(digit) digit")
        
        if digit == "0" && display.text == "0" {
            return
        }
        
        if digit == "." {
            // 이미 "."이 텍스트에 포함되어 있는 경우 입력을 무시
            if let text = display.text, text.contains(".") {
                return
            }
        }
        
        if touchDigit {
            if display.text == "0" {
                display.text = digit
            } else {
                display.text?.append(digit)
            }
        } else {
            display.text = digit
            touchDigit = true
        }
        
    }
    
    @IBAction func operatorTouched(_ sender: UIButton) {
        guard let symbol = sender.currentTitle else { return }
        print("Touched operator: \(symbol)")
        
        if let number = Double(display.text!) {
            firstNumber = number
            operatorSymbol = symbol
            touchDigit = false
        }
    }

    @IBAction func resultTouched(_ sender: UIButton) {
        if let number = Double(display.text!),
           let symbol = operatorSymbol,
           let result = calculator.calculate(firstNumber: firstNumber, operatorSymbol: symbol, secondNumber: number) {
            display.text = String(result)
            firstNumber = result
            touchDigit = false
        }
    }

    @IBAction func percent(_ sender: UIButton) {
        guard let digit = sender.currentTitle else { return }
        
        if digit == "0" && display.text == "0" {
            return
        }
        
        if touchDigit {
            if display.text == "0" {
                display.text = digit
            } else if let currentNumber = Double(display.text ?? "") {
                let percentValue = currentNumber * 0.01
                display.text = String(percentValue)
            }
        } else {
            touchDigit = false
        }
    }
    
    @IBAction func plusMinus(_ sender: UIButton) {
        guard let digit = sender.currentTitle else { return }
        
        if digit == "0" && display.text == "0" {
            return
        }
        
        if touchDigit {
            if display.text == "0" {
                display.text = digit
            } else if let currentNumber = Double(display.text ?? "") {
                let plusMinusValue = currentNumber * -1
                display.text = String(plusMinusValue)
            }
        } else {
            touchDigit = false
        }
    }
    
    
    
    @IBAction func clear(_ sender: UIButton) {
        display.text = "0"
        firstNumber = nil
        operatorSymbol = nil
        touchDigit = false
    }
    
}

Calculator.swift

import Foundation

class Calculator {
    
    func calculate(firstNumber: Double?, operatorSymbol: String, secondNumber: Double) -> Double? {
        guard let fn = firstNumber else {
            return nil
        }
        
    switch operatorSymbol {
    case "+":
        return fn + secondNumber
    case "-":
        return fn - secondNumber
    case "×":
        return fn * secondNumber
    case "÷":
        if secondNumber != 0 {
            return fn / secondNumber
        } else {
            print("ERROR: Division by zero")
            return nil
        }
    default:
        print("ERROR")
        return nil
        
        }
        
    }
    
}


이쯤에서 실패한 나의 코드를 공유한다.

// ViewController.swift

import UIKit

class ViewController: UIViewController {
    @IBOutlet weak var display: UILabel!
    
    var calculator = Calculator()
    var touchDigit = false
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    @IBAction func touch(_ sender: UIButton) {
        guard let digit = sender.currentTitle else { return }
        print("Touched \(digit) digit")
        
        if digit == "0" && display.text == "0" {
            // 숫자가 0이고 현재 표시된 값도 0일 경우 무시
            return
        }
        
        if touchDigit {
            if display.text == "0" {
                display.text = digit
            } else {
                display.text?.append(digit)
            }
        } else {
            display.text = digit
            touchDigit = true
        }
    }
    
    @IBAction func operatorTouched(_ sender: UIButton) {
        guard let operatorValue = sender.currentTitle else { return }
        print("Touched operator: \(operatorValue)")

        if let displayText = display.text,
           let displayValue = Double(displayText) {
            switch operatorValue {
            case "+" :
                calculator.add(displayValue)
            case "-" :
                calculator.subtract(displayValue)
            case "×" :
                calculator.multiply(by: displayValue)
            case "÷" :
                calculator.divide(by: displayValue)
            default :
                break
            }
        }
        touchDigit = false
    }
    
    
    @IBAction func clear(_ sender: UIButton) {
        display.text = "0"
        calculator.clear()
        touchDigit = false
    }
}


// Calculator.swift

import Foundation

class Calculator {
    var displayValue: Double = 0
    
    func add(_ digit: Double) {
        displayValue += digit
    }
    
    func subtract(_ digit: Double) {
        displayValue -= digit
    }
    
    func multiply(by digit: Double) {
        displayValue *= digit
    }
    
    func divide(by digit: Double) {
        guard digit != 0 else {
            print("Error: Division by zero")
            return
        }
        displayValue /= digit
    }
    func clear() {
        displayValue = 0
    }
}

위 코드는 숫자 값과 덧셈, 뺄셈만 되고 나눗셈, 곱셉은 구현이 안됐다.

// 두번째 Calculator.swift

import Foundation

class Calculator {
    
    var operand: Double?
    var opration: String?
    var displayValue: Double = 0

    
    func saveOperand(_ operand: Double) {
        self.operand = operand
    }
    
    func saveOpration(_ opration: String) {
        self.opration = opration
    }
    
    var result: Double? {
        guard let operand = operand,
              let opration = opration else {
            return nil
        }
        
        switch opration {
        case "+" :
            return operand + displayValue
        case "-" :
            return operand - displayValue
        case "×" :
            return operand * displayValue
        case "÷" :
            guard displayValue != 0 else {
                print("Error: Division by zero")
                return nil
            }
            return operand / displayValue
        default :
            return nil
        }
    }
    
    func clear() {
        operand = nil
        opration = nil
    }
    
}

혹시 return 으로 안받아서 그런가 싶어 재도전한 코드인데, 이것도 마찬가지고 심지어 얘는 연산이 다 안됐다.

😰
오늘은 사칙연산이 가동되는 계산기 어플을 만들어봤다.
어찌저찌 구현을 시켰는데, 내일은 실패한 코드들에 대한 리뷰를 진행해야겠다.
그리고 남은 2, 3번 과제에 대해 고민하고 코드를 완성하려고 한다.
하루종일 에러와 싸우고 챗GPT한테도 자문해봤다가 내 한계를 보기도 했는데 어찌 처음부터 다 잘되겠냐
앞으로 더 연습하고 밤을 새더라도 끈기를 갖고 좋은 개발자가 되도록 노력해야겠다!
체력도 놓치면 안된다. 운동도 해야 한다!!

profile
하지만 나는 끝까지 살아남을 거야!

0개의 댓글