iOS) 복구가능한 계산기 어플리케이션 만들기 (2) :: CushyCalculator

2021. 8. 8. 16:55iOS

이 포스팅은 이전글 에서 이어지는 시리즈 포스팅이다. 

 

iOS) 복구가능한 계산기 어플리케이션 만들기 (1) :: CushyCalculator

이전에 만들었던 어플리케이션을 회고하는겸 해서 리뷰해보려고 한다. 이 어플리케이션은 기본 계산기 기능 외에도 특별한 기능이 들어가 있는데 바로 삭제된 값을 복구하는 기능이다. 전체 코

codecrafting.tistory.com

 

연산자 버튼을 눌렀을 때 호출되는 operatorButton(sender:) 을 마저 이어서 설명한다.

 

일단 operatorButton의 전체코드다.

 

@IBAction func operatorButton(_ sender: UIButton) {
    
    if reloadTextField {
        return
    }
    guard let operType = sender.restorationIdentifier else {
        return
    }
    
    reloadTextField = true
    let numberInField = workField.text!

    if operStack.count == 1 {
        operStack.append(operType)
        return
    }
    
    if operStack.isEmpty {
        operStack.append(numberInField)
        operStack.append(operType)
    } else {
        operStack.append(numberInField)
        
        guard let first = Double(operStack[0]) else {
            return
        }
        guard let second = Double(operStack[2]) else {
            return
        }
        let middleOperator = operStack[1]
        
        var result: Double = Double(InitialCalculator)!
        switch middleOperator {
        case "plus":
            result = first + second
        case "minus":
            result = first - second
        case "multi":
            result = first * second
        case "divide":
            result = first / second
        default:
            break
        }
        
        var finRes: String = String(result)
        if result == ceil(result) {
            finRes.removeSubrange(finRes.firstIndex(of: ".")!..<finRes.endIndex)
        }
        if operType == "equalSign" {
            workField.text = finRes
            operStack.removeAll()
            operStack.append(workField.text!)
            reloadTextField = false
            return
        } else if operType != "equalSign" {
            operStack[1] = operType
        }
        
        workField.text = finRes
        operStack[0] = finRes
    
        operStack.removeLast()
    }
}

 

if operStack.isEmpty { .. } 부터 설명하겠다. 

 

연산자 버튼을 눌렀을 때, operStack이 비어있는 경우는 어떤 상황일까?

처음 계산기를 켜고 숫자를 입력하고 연산자 버튼을 눌렀을 때 operStack은 비어있는 상태일 것이다.

 

따라서 만약에 10을 입력하고 "+"를 눌렀다면 operStack에는 10과 +가 저장되어야 한다.

 

그렇다면, operStack이 비어있지 않은 경우는 어떤 상황일까?

 

이전 포스팅에서 설명한 것처럼 연산자가 가장 앞에 있는 상황이나 연산자가 두번연속 중복되어 입력되는 예외상황은 전부 처리를 해줬으니 남은 케이스는 위에서처럼 10과 + 만 있는 상황일 것이다.

 

이때 (연산자를 눌렀을때) 는 현재 화면에 표시된 숫자를 operStack에 넣고 실제적인 계산을 하면 된다.

다시 예를 들어서 설명하자면, operStack에 10과 +만 있는 상황에서 화면에 20이 있고 어떤 연산자를 눌렀다면 operStack에는 일단 10과 +와 20이 들어가야 한다. 눌렀던 연산자 버튼은 10+20 의 결과값 30 뒤에 붙어야 하기때문에 일단 처리를 미뤄둔다.

이제 operStack의 첫번째 숫자를 first라고 명하고 operStack의 세번째 숫자를 second라고 명한다. 이 두 숫자 사이의 연산자에 따라서 계산결과를 달리해야 하므로 다음과 같이 switch 로 분기하여 처리한다.

 

guard let first = Double(operStack[0]) else {
    return
}
guard let second = Double(operStack[2]) else {
    return
}
let middleOperator = operStack[1]

var result: Double = Double(InitialCalculator)!
switch middleOperator {
case "plus":
    result = first + second
case "minus":
    result = first - second
case "multi":
    result = first * second
case "divide":
    result = first / second
default:
    break
}

이때의 결과값을 result라고 한다. 이 result는 Double형식을 가지게 되어있는데 Int타입으로 처리하면 나눌셈을 할 수 없기 때문. 하지만 이때문에 10+20 에 대한 결과 30도 result에는 30.0으로 표시된다.

 

따라서 result에 대한 후처리가 필요하다.

 

var finRes: String = String(result)
if result == ceil(result) {
    finRes.removeSubrange(finRes.firstIndex(of: ".")!..<finRes.endIndex)
}

먼저 result를 finRes라는 이름으로 따로 저장해두되 String타입으로 치환하여 저장한다. 이것은 결과값이 자연수임에도 소숫점이 붙어있는 경우 이것을 제거해 주기 위한 것이다.

 

예를 들어 설명해보자면 result에는 30.0이 들어가 있을때, result를 올림(ceil) 하면 30이다. 즉 30.0(result) 이나 30(ceil 처리된 result) 이 같다면 finRes에서 소숫점 이하자리 30.0을 제거하고 finRes에는 30만 가지게 된다.

 

만약에 28.9 라는 result가 나왔다고 가정해보면 ceil(result)의 값은 29이다. 28.9와 29는 같지 않으므로 finRes는 여전히 28.9다.

 

여기까지 했으면 아까 입력한 연산자에 대한 처리가 필요하다.

if operType == "equalSign" {
    workField.text = finRes
    operStack.removeAll()
    operStack.append(workField.text!)
    reloadTextField = false
    return
} else if operType != "equalSign" {
    operStack[1] = operType
}

이때 이 연산자가 Equal (=) 이느냐 아니냐에 따라 분기된다.

 

만약 연산자가 Equal 이라면, 아까 처리해둔 finRes의 값을 화면에 표시하고, operStack에 있는 모든 값을 지운다. 그리고 방금 화면에 표시한 숫자를 operStack의 가장 첫번째 인덱스에 위치시키고 reloadTextField를 false로 처리한다. reloadTextField를 false로 처리함에 따라 다른 연산자를 곧이어 처리할 수 있게된다.

 

if operType == "equalSign" {
    ...
} else if operType != "equalSign" {
    operStack[1] = operType
}

workField.text = finRes
operStack[0] = finRes

operStack.removeLast()

 

하지만 만약 연산자가 Equal 이 아니라면 해당 연산자를 operStack의 두번째 인덱스에 위치시키고

 

아까 계산해둔 finRes의 값을 operStack의 첫번째로 위치시킨뒤

가장 뒤에 있는 숫자를 지운다.

이렇게 하면 곧이어 다른 숫자를 넣을 수 있게된다. 

 

 

이렇게 해서 기본적인 사칙연산의 기능은 모두 수행하도록 만들었다.

남은 기능은 퍼센트(%) 기능과 음양수(+_) 전환기능, 그리고 삭제(AC)기능이다.

 

@IBAction func percentBtn(_ sender: UIButton) {
    if let num = workField.text {
        let stringifiedNum:Double = Double(num)! * 0.01
        workField.text = String(stringifiedNum)
    }
}

@IBAction func plusMinusBtn(_ sender: UIButton) {
    if let num = workField.text {
        workField.text = String(Double(num)! * -1)
        if operStack.count == 1 {
            operStack[0] = workField.text!
        }
    }
}

@IBAction func clearBtn(_ sender: UIButton) {
    DeletedData.deleted.append(workField.text!)
    workField.text = InitialCalculator
    operStack.removeAll()
    isDotContains = false
}

퍼센트와 음양수전환은 쉬우니까 설명은 생략하겠다.

삭제기능은 operStack을 비움과 동시에 DeletedData 클래스의 deleted라는 배열에 지운 값을 추가하는데 이는 삭제된 값들을 불러올 때 사용하기 위함이다.

 

import Foundation
class DeletedData {
    static var deleted:[String] = []
}

 

 

역시나 이번에도 분량조절에 실패할것 같으므로 여기서 끊고 다음 포스팅에서 마무리 짓도록 하겠다.

 

 

 

👋