Issue with Implementing Additional Buttons in SwiftUI Calculator

I'm working on a calculator app in SwiftUI, and I'm trying to add some additional buttons to incorporate advanced mathematical functions. I've already implemented the basic calculator functionality, but I'm facing issues with making these new buttons work as expected.


The additional buttons I'm trying to implement are:

Trigonometric Functions: sin, cos, tan, arcsin, arccos, arctan

Exponential and Logarithmic Functions: e^x, ln, log

Power and Root Functions: x^2, x^3, √x, ³√x, x^y

Hyperbolic Functions: sinh, cosh, tanh, arcsinh, arccosh, arctanh Other Functions: abs, factorial, percent


Could someone please guide me on how to properly integrate these buttons into the calculator functionality? Any insights or suggestions would be greatly appreciated.

Here's the code I have so far, which includes the basic calculator functionality and the additional buttons I'm trying to incorporate:

struct ContentView: View {
    @State private var input: String = ""
    @State private var result: String = ""

    var body: some View {
        VStack {
            Text(result)
                .font(.largeTitle)
                .padding()

            HStack {
                CalculatorButton(title: "sin", action: { appendToInput("sin(") })
                CalculatorButton(title: "cos", action: { appendToInput("cos(") })
                CalculatorButton(title: "tan", action: { appendToInput("tan(") })
                CalculatorButton(title: "arcsin", action: { appendToInput("asin(") })
            }

            HStack {
                CalculatorButton(title: "arccos", action: { appendToInput("acos(") })
                CalculatorButton(title: "arctan", action: { appendToInput("atan(") })
                CalculatorButton(title: "e^x", action: { appendToInput("exp(") })
                CalculatorButton(title: "ln", action: { appendToInput("log(") })
            }

            HStack {
                CalculatorButton(title: "log", action: { appendToInput("log10(") })
                CalculatorButton(title: "x^2", action: { appendToInput("pow(") })
                CalculatorButton(title: "x^3", action: { appendToInput("pow(") })
                CalculatorButton(title: "√x", action: { appendToInput("sqrt(") })
            }

            HStack {
                CalculatorButton(title: "³√x", action: { appendToInput("pow(") })
                CalculatorButton(title: "x^y", action: { appendToInput("^") })
                CalculatorButton(title: "sinh", action: { appendToInput("sinh(") })
                CalculatorButton(title: "cosh", action: { appendToInput("cosh(") })
            }

            HStack {
                CalculatorButton(title: "tanh", action: { appendToInput("tanh(") })
                CalculatorButton(title: "arcsinh", action: { appendToInput("asinh(") })
                CalculatorButton(title: "arccosh", action: { appendToInput("acosh(") })
                CalculatorButton(title: "arctanh", action: { appendToInput("atanh(") })
            }

            HStack {
                CalculatorButton(title: "π", action: { appendToInput("pi") })
                CalculatorButton(title: "e", action: { appendToInput("exp(1)") })
                CalculatorButton(title: "abs", action: { appendToInput("abs(") })
                CalculatorButton(title: "factorial", action: { appendToInput("factorial(") })
            }

            HStack {
                CalculatorButton(title: "%", action: { appendToInput("%") })
                CalculatorButton(title: "+", action: { appendToInput("+") })
                CalculatorButton(title: "-", action: { appendToInput("-") })
                CalculatorButton(title: "*", action: { appendToInput("*") })
            }

            HStack {
                CalculatorButton(title: "/", action: { appendToInput("/") })
                CalculatorButton(title: ".", action: { appendToInput(".") })
                CalculatorButton(title: "Clear", action: { clearInput() })
                CalculatorButton(title: "=", action: { evaluateExpression() })
            }
        }
        .padding()
    }

    private func appendToInput(_ value: String) {
        input.append(value)
    }

    private func clearInput() {
        input = ""
        result = ""
    }

    private func evaluateExpression() {
        let expression = NSExpression(format: input)
        if let evaluatedValue = expression.expressionValue(with: nil, context: nil) as? NSNumber {
            result = evaluatedValue.stringValue
        } else {
            result = "Invalid expression"
        }
    }
}

struct CalculatorButton: View {
    let title: String
    let action: () -> Void

    var body: some View {
        Button(action: action) {
            Text(title)
                .font(.title)
                .frame(width: 80, height: 80)
                .foregroundColor(.white)
                .background(Color.blue)
                .cornerRadius(40)
        }
    }
}
Answered by Claude31 in 755851022

There is no way to enter digits ?

You have to change the evaluation, which depends on what you evaluate: constant, function, …

I have modified a few lines (marked with ###) to make pi, x^2 and x^3 work.

Use it as a template for other modifications.

struct ContentView: View {
    @State private var input: String = ""
    @State private var result: String = ""

    var body: some View {
        VStack {
            Text(result)
                .font(.largeTitle)
                .padding()

            HStack {
                CalculatorButton(title: "sin", action: { appendToInput("sin(") })
                CalculatorButton(title: "cos", action: { appendToInput("cos(") })
                CalculatorButton(title: "tan", action: { appendToInput("tan(") })
                CalculatorButton(title: "arcsin", action: { appendToInput("asin(") })
            }

            HStack {
                CalculatorButton(title: "arccos", action: { appendToInput("acos(") })
                CalculatorButton(title: "arctan", action: { appendToInput("atan(") })
                CalculatorButton(title: "e^x", action: { appendToInput("exp(") })
                CalculatorButton(title: "ln", action: { appendToInput("log(") })
            }

            HStack {
                CalculatorButton(title: "log", action: { appendToInput("log10(") })
                CalculatorButton(title: "x^2", action: { appendToInput("pow2") })   // ####
                CalculatorButton(title: "x^3", action: { appendToInput("pow3") })   // ####
                CalculatorButton(title: "√x", action: { appendToInput("sqrt(") })
            }

            HStack {
                CalculatorButton(title: "³√x", action: { appendToInput("pow(") })
                CalculatorButton(title: "x^y", action: { appendToInput("^") })
                CalculatorButton(title: "sinh", action: { appendToInput("sinh(") })
                CalculatorButton(title: "cosh", action: { appendToInput("cosh(") })
            }

            HStack {
                CalculatorButton(title: "tanh", action: { appendToInput("tanh(") })
                CalculatorButton(title: "arcsinh", action: { appendToInput("asinh(") })
                CalculatorButton(title: "arccosh", action: { appendToInput("acosh(") })
                CalculatorButton(title: "arctanh", action: { appendToInput("atanh(") })
            }

            HStack {
                CalculatorButton(title: "π", action: { appendToInput("pi") })
                CalculatorButton(title: "e", action: { appendToInput("exp(1)") })
                CalculatorButton(title: "abs", action: { appendToInput("abs(") })
                CalculatorButton(title: "factorial", action: { appendToInput("factorial(") })
            }

            HStack {
                CalculatorButton(title: "%", action: { appendToInput("%") })
                CalculatorButton(title: "+", action: { appendToInput("+") })
                CalculatorButton(title: "-", action: { appendToInput("-") })
                CalculatorButton(title: "*", action: { appendToInput("*") })
            }

            HStack {
                CalculatorButton(title: "/", action: { appendToInput("/") })
                CalculatorButton(title: ".", action: { appendToInput(".") })
                CalculatorButton(title: "Clear", action: { clearInput() })
                CalculatorButton(title: "=", action: { evaluateExpression() })
            }
        }
        .padding()
    }

    private func appendToInput(_ value: String) {
        input = value // #### input.append(value) // But we keep result
    }

    private func clearInput() {
        input = ""
        result = ""
    }

    private func evaluateExpression() { // ###
        print(input)  // ### To understand what's going on
        switch input {
            case "pi":
                let myDouble = Double.pi
                let myFormulaDouble = "myDouble"
                let doubleElements = ["myDouble": myDouble]
                
                let expression = NSExpression(format: myFormulaDouble)
                
                if let evaluatedValue = expression.expressionValue(with: doubleElements, context: nil) as? NSNumber {
                    result = evaluatedValue.stringValue
                } else {
                    result = "Invalid expression"
                }
                
            case "pow2", "pow3":
                // data to elevate to power is result
                guard let val = Double(result) else { return }
                
                let myDouble = pow(val, input == "pow2" ? 2 : 3) // val * val
                let myFormulaDouble = "myDouble"
                let doubleElements = ["myDouble": myDouble]
                
                let expression = NSExpression(format: myFormulaDouble)
                
                if let evaluatedValue = expression.expressionValue(with: doubleElements, context: nil) as? NSNumber {
                    result = evaluatedValue.stringValue
                } else {
                    result = "Invalid expression"
                }

                
            default:
                print("default", input, result) // ### To understand what's going on
                let expression = NSExpression(format: input)
                if let evaluatedValue = expression.expressionValue(with: nil, context: nil) as? NSNumber {
                    result = evaluatedValue.stringValue
                } else {
                    result = "Invalid expression"
                }
        }
    }
}

struct CalculatorButton: View {
    let title: String
    let action: () -> Void

    var body: some View {
        Button(action: action) {
            Text(title)
                .font(.title)
                .frame(width: 80, height: 80)
                .foregroundColor(.white)
                .background(Color.blue)
                .cornerRadius(40)
        }
    }
}

What is the problem ? Adding buttons or defining how they work ?

I'm having trouble with the additional calculator buttons for trigonometric and exponential/logarithmic functions. The trigonometric functions (sin, cos, tan, arcsin, arccos, arctan) and exponential/logarithmic functions (e^x, ln, log) aren't working correctly when I append their respective function strings to the input. I've included a minimal reproducible example in my question. Any guidance on integrating these buttons properly would be appreciated. Thanks!

The functions that you are having a problem with, all append an opening bracket, "(".
But nowhere in your code do you supply the closing bracket, ")".
So the expression that you build is invalid.

After you have completed the numeric input, if the expression contains an unmatched "(", you must then add the closing ")".

You will probably need to add some extra state, to allow this.

Accepted Answer

There is no way to enter digits ?

You have to change the evaluation, which depends on what you evaluate: constant, function, …

I have modified a few lines (marked with ###) to make pi, x^2 and x^3 work.

Use it as a template for other modifications.

struct ContentView: View {
    @State private var input: String = ""
    @State private var result: String = ""

    var body: some View {
        VStack {
            Text(result)
                .font(.largeTitle)
                .padding()

            HStack {
                CalculatorButton(title: "sin", action: { appendToInput("sin(") })
                CalculatorButton(title: "cos", action: { appendToInput("cos(") })
                CalculatorButton(title: "tan", action: { appendToInput("tan(") })
                CalculatorButton(title: "arcsin", action: { appendToInput("asin(") })
            }

            HStack {
                CalculatorButton(title: "arccos", action: { appendToInput("acos(") })
                CalculatorButton(title: "arctan", action: { appendToInput("atan(") })
                CalculatorButton(title: "e^x", action: { appendToInput("exp(") })
                CalculatorButton(title: "ln", action: { appendToInput("log(") })
            }

            HStack {
                CalculatorButton(title: "log", action: { appendToInput("log10(") })
                CalculatorButton(title: "x^2", action: { appendToInput("pow2") })   // ####
                CalculatorButton(title: "x^3", action: { appendToInput("pow3") })   // ####
                CalculatorButton(title: "√x", action: { appendToInput("sqrt(") })
            }

            HStack {
                CalculatorButton(title: "³√x", action: { appendToInput("pow(") })
                CalculatorButton(title: "x^y", action: { appendToInput("^") })
                CalculatorButton(title: "sinh", action: { appendToInput("sinh(") })
                CalculatorButton(title: "cosh", action: { appendToInput("cosh(") })
            }

            HStack {
                CalculatorButton(title: "tanh", action: { appendToInput("tanh(") })
                CalculatorButton(title: "arcsinh", action: { appendToInput("asinh(") })
                CalculatorButton(title: "arccosh", action: { appendToInput("acosh(") })
                CalculatorButton(title: "arctanh", action: { appendToInput("atanh(") })
            }

            HStack {
                CalculatorButton(title: "π", action: { appendToInput("pi") })
                CalculatorButton(title: "e", action: { appendToInput("exp(1)") })
                CalculatorButton(title: "abs", action: { appendToInput("abs(") })
                CalculatorButton(title: "factorial", action: { appendToInput("factorial(") })
            }

            HStack {
                CalculatorButton(title: "%", action: { appendToInput("%") })
                CalculatorButton(title: "+", action: { appendToInput("+") })
                CalculatorButton(title: "-", action: { appendToInput("-") })
                CalculatorButton(title: "*", action: { appendToInput("*") })
            }

            HStack {
                CalculatorButton(title: "/", action: { appendToInput("/") })
                CalculatorButton(title: ".", action: { appendToInput(".") })
                CalculatorButton(title: "Clear", action: { clearInput() })
                CalculatorButton(title: "=", action: { evaluateExpression() })
            }
        }
        .padding()
    }

    private func appendToInput(_ value: String) {
        input = value // #### input.append(value) // But we keep result
    }

    private func clearInput() {
        input = ""
        result = ""
    }

    private func evaluateExpression() { // ###
        print(input)  // ### To understand what's going on
        switch input {
            case "pi":
                let myDouble = Double.pi
                let myFormulaDouble = "myDouble"
                let doubleElements = ["myDouble": myDouble]
                
                let expression = NSExpression(format: myFormulaDouble)
                
                if let evaluatedValue = expression.expressionValue(with: doubleElements, context: nil) as? NSNumber {
                    result = evaluatedValue.stringValue
                } else {
                    result = "Invalid expression"
                }
                
            case "pow2", "pow3":
                // data to elevate to power is result
                guard let val = Double(result) else { return }
                
                let myDouble = pow(val, input == "pow2" ? 2 : 3) // val * val
                let myFormulaDouble = "myDouble"
                let doubleElements = ["myDouble": myDouble]
                
                let expression = NSExpression(format: myFormulaDouble)
                
                if let evaluatedValue = expression.expressionValue(with: doubleElements, context: nil) as? NSNumber {
                    result = evaluatedValue.stringValue
                } else {
                    result = "Invalid expression"
                }

                
            default:
                print("default", input, result) // ### To understand what's going on
                let expression = NSExpression(format: input)
                if let evaluatedValue = expression.expressionValue(with: nil, context: nil) as? NSNumber {
                    result = evaluatedValue.stringValue
                } else {
                    result = "Invalid expression"
                }
        }
    }
}

struct CalculatorButton: View {
    let title: String
    let action: () -> Void

    var body: some View {
        Button(action: action) {
            Text(title)
                .font(.title)
                .frame(width: 80, height: 80)
                .foregroundColor(.white)
                .background(Color.blue)
                .cornerRadius(40)
        }
    }
}
Issue with Implementing Additional Buttons in SwiftUI Calculator
 
 
Q