Search code examples
iosswiftswiftuifocustextfield

How to change the value of a focused textfield in SwiftUI?


I have a focused textfield.

@FocusState private var focusedField: Field?
TextField("0", value: $price, format: .currency(code: "USD").locale(Locale(identifier: "en_US")))
            .keyboardType(.numbersAndPunctuation)
            .multilineTextAlignment(.trailing)
            .focused($focusedField, equals: field)

And a button

Button("Tap") { price = 200 }

However if the textfield is focused you can not use the button to rewrite the value in the textfield. The textfield needs to be unfocused to be able to do that.

When I rewrite the code:

Button("Tap") {
    focusedField = nil
    price = 200
}

It will dismiss the keyboard, but still will not rewrite the value in the textfield. Only after the keyboard is dismissed if I tap the button again will it rewrite the value in the textfield.

How can I make this work?


Solution

  • Based on my comment, you could try this alternative approach, using a String, as shown in this example code.

    Since in a TextField if the value is a string, the text field updates this value continuously.... For non-string types, it updates the value when the user commits their edits, such as by pressing the Return key.

    Works well for me. It is a few more lines of code, but at least there is no ad-hockery of waiting 0.1 sec.

    enum Field: Hashable {
         case price
     }
    
    struct ContentView: View {
        var body: some View {
            TestView()
        }
    }
    
    struct TestView: View {
        @State private var txt = ""
        @State private var price = 0.0
        
        @FocusState private var focusedField: Field?
        
        func asCurrency(_ str: String) -> String {
            if let value = Double(str) {
                let frmt = NumberFormatter()
                frmt.numberStyle = .currency
                frmt.maximumFractionDigits = 2
                frmt.minimumFractionDigits = 2
                frmt.locale = Locale(identifier: "en_US")
                if let str = frmt.string(for: value) {
                    return str
                }
            }
            return ""
        }
    
        var body: some View {
            TextField(asCurrency("0"), text: Binding(
                get: { asCurrency(txt) },
                set: {
                    if let d = Double($0) {
                        price = d
                        txt = $0
                    }
                }))
            .keyboardType(.numbersAndPunctuation)
            .multilineTextAlignment(.trailing)
            .focused($focusedField, equals: .price)
            
            Button("Tap") {
                focusedField = nil
                price = 200.0
                txt = String(price)
            }
        }
    }