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
}
}