'Recognize changes in SwiftUI TextField with double value

I am using a TextField to let the user add a price for something. To prevent the user adding other values as a number, I change the keyboard type to .decimalPad.

The stringValue is updated correct with the binding on every character change. My problem now is, that the doubleValue is only updated, when the user presses the enter to commit. Unfortunately the decimal pad has no enter key to commit the input. So when I try to save the value to my core data object without pressing enter, 0 is saved instead the visible value (for example 17.12) in the text field. For testing now I will use another keyboard type with an enter button but for production this needs to be fixed somehow.

Do you have any ideas how this can be solved? Below a minimized example code to show the problem.

struct ContentView: View {
@State private var doubleValue: Double = 0.0
@State private var stringValue = "Test"

private let numberFormatter: NumberFormatter = {
    let numberFormatter = NumberFormatter()
    numberFormatter.numberStyle = .currency
    return numberFormatter
}()

var body: some View {
    Form {
        TextField("", text: $stringValue)
            .disableAutocorrection(true)
        HStack {
            Text("Price")
            Spacer()
            TextField("", value: $doubleValue, formatter: numberFormatter)
                .fixedSize()
                .keyboardType(.decimalPad)
        }
        // show live value changes
        Text("\(stringValue)")
        Text("Value: \(doubleValue)")
    }
    .padding()
}
func saveToCoreData() {
    // save values into core data
}
}

I am using Xcode 12.2 and MacOS BigSur 11.0.1.

Edit belonging to asperis comment: Your shared solution looks quite good, but as you can see in the screenshot it works not really correct: numbertextfield_test

After initial setup with value 0 I entered a 2 and then a 3. The shown value is 2.03 which is indeed not what I wanted. Maybe I can enhance the approach of your hint.



Solution 1:[1]

On iOS 15 you can use TextField ("Hello", value: $someDoubleValue, formatter: <your NumberFormatter here>) and the value propagates fine.

On iOS 14, it doesn't seem to work binding a TextField to a Double value with a formatter.

So the following works fine in iOS 15, but does not work in iOS 14.

Note The double value is nested here because this is a code snippet taken from what I'm working on right now.

public class MyViewModel: ObservableObject {
    @Published var child: ChildObject
}

public class ChildObject: Identifiable, ObservableObject {
    @Published var doubleValue: Double
}

public struct FancyPantsView: View {
     @ObservedObject var viewModel: MyViewModel

     public var body: some View {
          VStack {
               TextField ("Hello", value: $viewModel.child.doubleValue, formatter: amountFormatter)
                    .keyboardType(.decimalPad)
          }
     }

     let amountFormatter: NumberFormatter = {
          let formatter = NumberFormatter()
          formatter.zeroSymbol = ""
          return formatter
     }()
}

Solution 2:[2]

I had similar issue. This is the solution I implemented since you cannot detect typed value if it is not string. The idea is to have value as string and then convert it to Double before processing the value further. If you are passing Double value, you need to convert it to String before binding it to TextField.

struct SomeTextField: View {
    @State var string: String = ""
    @State var double: Double = 0.0
    
    func save() {
        double = Double(string)!
    }

    var body: some View {
        VStack {
            Text("String: \(string) Double: \(String(double))")
            TextField("0.00", text: $string).keyboardType(.decimalPad)
            Button(action: save) { Text("Save") }
        }
    }
        

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 spnkr
Solution 2 SuRao