Home > database >  Swiftui Calculator Continuous calculation
Swiftui Calculator Continuous calculation

Time:06-01

I want to practice the real ios calculator in my app and can calculate continuously

When I tap add button, I want to can be calculated.But now my value can't really be calculated automatically when I tap add button.I don't have any idea about this issue.

How should I change my code?

here's my ContentView.swift:

import SwiftUI

enum CalcButton: String {
    case one = "1"
    case two = "2"
    case three = "3"
    case four = "4"
    case five = "5"
    case six = "6"
    case seven = "7"
    case eight = "8"
    case nine = "9"
    case zero = "0"
    case add = " "
    case subtract = "-"
    case divide = "รท"
    case mutliply = "x"
    case equal = "="
    case clear = "AC"
    case decimal = "."
    case percent = "%"
    case negative = "-/ "

    var buttonColor: Color {
        switch self {
        case .add, .subtract, .mutliply, .divide, .equal:
            return .orange
        case .clear, .negative, .percent:
            return Color(.lightGray)
        default:
            return Color(UIColor(red: 55/255.0, green: 55/255.0, blue: 55/255.0, alpha: 1))
        }
    }
}

enum Operation {
    case add, subtract, multiply, divide, none
}

struct ContentView: View {

    @State var value = "0"
    @State var runningNumber = 0
    @State var currentOperation: Operation = .none

    let buttons: [[CalcButton]] = [
        [.clear, .negative, .percent, .divide],
        [.seven, .eight, .nine, .mutliply],
        [.four, .five, .six, .subtract],
        [.one, .two, .three, .add],
        [.zero, .decimal, .equal],
    ]

    var body: some View {
        ZStack {
            Color.black.edgesIgnoringSafeArea(.all)

            VStack {
                Spacer()

                // Text display
                HStack {
                    Spacer()
                    Text(value)
                        .bold()
                        .font(.system(size: 100))
                        .foregroundColor(.white)
                }
                .padding()

                // Our buttons
                ForEach(buttons, id: \.self) { row in
                    HStack(spacing: 12) {
                        ForEach(row, id: \.self) { item in
                            Button(action: {
                                self.didTap(button: item)
                            }, label: {
                                Text(item.rawValue)
                                    .font(.system(size: 32))
                                    .frame(
                                        width: self.buttonWidth(item: item),
                                        height: self.buttonHeight()
                                    )
                                    .background(item.buttonColor)
                                    .foregroundColor(.white)
                                    .cornerRadius(self.buttonWidth(item: item)/2)
                            })
                        }
                    }
                    .padding(.bottom, 3)
                }
            }
        }
    }

    func didTap(button: CalcButton) {
        switch button {
        case .add, .subtract, .mutliply, .divide, .equal:
            if button == .add {
                self.currentOperation = .add
                self.runningNumber = Int(self.value) ?? 0
                self.value = "\(runningNumber   Int(self.value) ?? 0)"
                
            }
            else if button == .subtract {
                self.currentOperation = .subtract
                self.runningNumber = Int(self.value) ?? 0
            }
            else if button == .mutliply {
                self.currentOperation = .multiply
                self.runningNumber = Int(self.value) ?? 0
            }
            else if button == .divide {
                self.currentOperation = .divide
                self.runningNumber = Int(self.value) ?? 0
            }
            else if button == .equal {
                let runningValue = self.runningNumber
                let currentValue = Int(self.value) ?? 0
                switch self.currentOperation {
                case .add: self.value = "\(runningValue   currentValue)"
                case .subtract: self.value = "\(runningValue - currentValue)"
                case .multiply: self.value = "\(runningValue * currentValue)"
                case .divide: self.value = "\(runningValue / currentValue)"
                case .none:
                    break
                }
            }

            if button != .equal {
                self.value = "0"
            }
        case .clear:
            self.value = "0"
        case .decimal, .negative, .percent:
            break
        default:
            let number = button.rawValue
            if self.value == "0" {
                value = number
            }
            else {
                self.value = "\(self.value)\(number)"
            }
        }
    }

    func buttonWidth(item: CalcButton) -> CGFloat {
        if item == .zero {
            return ((UIScreen.main.bounds.width - (4*12)) / 4) * 2
        }
        return (UIScreen.main.bounds.width - (5*12)) / 4
    }

    func buttonHeight() -> CGFloat {
        return (UIScreen.main.bounds.width - (5*12)) / 4
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

CodePudding user response:

That was trickier than expected. A regular calculator also shows partial results when you press the next operator ( , -, * etc.) – not only on "=". So each time a operator is tapped, you have to calculate the partialResult.

I had to introduce a new var AwaitingNewNumberInput to handle this case, where you see a partial result, but when pressing a number you start a new number entry.

Excuse me, but I also renamed the vars for more clarity (at least for myself). And I kept all calculation vars as Int to reduce conversion from String.

struct ContentView: View {
    
    @State var displayNumber = 0 // change to Int
    @State var lastResult = 0
    @State var currentOperation: Operation = .none
    @State var awaitingNewNumberInput = true // to switch from result display to input

    let buttons: [[CalcButton]] = [
        [.clear, .negative, .percent, .divide],
        [.seven, .eight, .nine, .mutliply],
        [.four, .five, .six, .subtract],
        [.one, .two, .three, .add],
        [.zero, .decimal, .equal],
    ]
    
    var body: some View {
        ZStack {
            Color.black.edgesIgnoringSafeArea(.all)
            
            VStack {
                Spacer()
                
                // Text display
                HStack {
                    Spacer()
                    Text("\(displayNumber)") // show value as string
                        .font(.system(size: 100))
                        .foregroundColor(.white)
                }
                .padding()
                
                // Our buttons
                ForEach(buttons, id: \.self) { row in
                    HStack(spacing: 12) {
                        ForEach(row, id: \.self) { item in
                            Button(action: {
                                didTap(button: item)
                            }, label: {
                                Text(item.rawValue)
                                    .font(.system(size: 32))
                                    .frame(
                                        width: buttonWidth(item: item),
                                        height: buttonHeight()
                                    )
                                    .background(item.buttonColor)
                                    .foregroundColor(.white)
                                    .cornerRadius(buttonWidth(item: item)/2)
                            })
                        }
                    }
                    .padding(.bottom, 3)
                }
            }
        }
    }
    
    func didTap(button: CalcButton) {
        
        
        switch button {
        case .add:
            setCurrentResult()
            currentOperation = .add
            awaitingNewNumberInput = true

        case .subtract:
            setCurrentResult()
            currentOperation = .subtract
            awaitingNewNumberInput = true

        case .mutliply:
            setCurrentResult()
           currentOperation = .multiply
            awaitingNewNumberInput = true

        case .divide:
            setCurrentResult()
            currentOperation = .divide
            awaitingNewNumberInput = true

        case .equal:
            setCurrentResult()
            currentOperation = .none
            awaitingNewNumberInput = true

        case .clear:
            // set ALL to 0
            displayNumber = 0
            lastResult = 0
            currentOperation = .none
            awaitingNewNumberInput = true

        case .decimal, .negative, .percent:
            break
            
        default:
            // number input
            let number = Int(button.rawValue) ?? 0 // string to Int
            if awaitingNewNumberInput {
                awaitingNewNumberInput = false
                displayNumber = number
            } else {
                displayNumber = displayNumber * 10   number
            }
        }
    }
    
    
    // check if an older operation is active, calculate partial result
    func setCurrentResult() {
        switch currentOperation {
        case .add:
            displayNumber  = lastResult
        case .subtract:
            displayNumber = lastResult - displayNumber
        case .multiply:
            displayNumber *= lastResult
        case .divide:
            displayNumber = lastResult / displayNumber
        case .none:
            break
        }
        lastResult = displayNumber
        currentOperation = .none
    }
    
    
    func buttonWidth(item: CalcButton) -> CGFloat {
        if item == .zero {
            return ((UIScreen.main.bounds.width - (4*12)) / 4) * 2
        }
        return (UIScreen.main.bounds.width - (5*12)) / 4
    }
    
    func buttonHeight() -> CGFloat {
        return (UIScreen.main.bounds.width - (5*12)) / 4
    }
}
  • Related