Reputation: 103
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
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
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)")
}
}
}
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 TextField
s.
Upvotes: 5