Johan Kornet
Johan Kornet

Reputation: 103

Read and make calculations with textfield input SwiftUI

I want to make calculations with inputs from 2 different Textfields and put the output in a Text. See code:

@State var input1: String = ""

@State var input2: String = ""

var calculation : Double {
    let calculationProduct = Double(input1) * Double(input2)
    return calculationProduct
}

var body: some View {
VStack{
 TextField("", text: $input1)
 TextField("", text: $input1)

Text("\(calculation)")
}

The problem is the code won't compile, i get different compile errors, for example: "Binary operator '*' cannot be applied to two 'Double?' operands".

What goes wrong?

Upvotes: 3

Views: 1778

Answers (2)

gary
gary

Reputation: 85

@Johankornet, you have probably figured this out by now, but the below code will work for decimal entries:

import SwiftUI

struct ContentView: View {
    @State var textFieldEntry: Double = 1.0

    let formatter: NumberFormatter = {
            let formatter = NumberFormatter()
            formatter.numberStyle = .decimal
            return formatter
        }()

    var body: some View {
        VStack {
            TextField("title", value: $textFieldEntry, formatter:        formatter).padding()
        }
    }
}

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

To see it working, i made an entire app with this main file:

import SwiftUI

@main
struct testTextFieldWithNumericFormattingApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

formatter.decimalstyle = .decimal restricts the TextField input to decimals only. Type in something thats not a decimal, like "abc", hit return and the input is ignored. This input field can accept scientific notation numbers as input, but the TextField will display as a decimal so very small scientific notation inputs, such as 3e-7 will be displayed as zero.

@JohanKornet, you have probably found other more elegant solutions for this problem by now, but if not maybe this will help:)

Upvotes: 2

staticVoidMan
staticVoidMan

Reputation: 20234

Double(input1) returns String? because it's not guaranteed to work. e.g. Double("1abc")

We can use guard let or if let or even a nil coalescing operator ?? to handle this. But for the following example we will gracefully handle it using guard let.

struct ContentView: View {
    @State var input1: String = ""
    @State var input2: String = ""

    var calculation : Double {
        guard let m = Double(input1), let n  = Double(input2) else { return 0 }
        return m * n
    }

    var body: some View {
        VStack {
            TextField("", text: $input1)
            TextField("", text: $input2)

            Text("\(calculation)")
        }
    }
}

EDIT

As per your comments, there are multiple ways to show "Error" on invalid inputs, or the answer upto 2 decimal point.
For this example, lets change the result to a computed String property both these these cases, as such:

struct ContentView: View {
    @State var input1: String = ""
    @State var input2: String = ""

    var calculation: String {
        //check if both fields have text else no need for message
        guard input1.isEmpty == false, input2.isEmpty == false else { return "" }

        //check if both are numbers else we need to print "Error"
        guard let m = Double(input1), let n  = Double(input2) else { return "Error" }

        let product = m * n
        return String(format: "%.2f", product)
    }

    var body: some View {
        VStack {
            TextField("Enter First Number", text: $input1)
                .textFieldStyle(RoundedBorderTextFieldStyle())
            TextField("Enter Second Number", text: $input2)
                .textFieldStyle(RoundedBorderTextFieldStyle())

            Text(calculation)
        }
    }
}

PS: If you want to ensure only numbers can be typed then you should think about applying the .keyboardType(.decimalPad) modifier on the TextFields.

Upvotes: 5

Related Questions