Why is TextField in SwiftUI not updating when set in other locale? - swiftui

#State private var entryPrice: Double?
TextField("0", value: $entryPrice, format: .currency(code: "USD")) // DOES NOT WORK
TextField("0", value: $entryPrice, format: .currency(code: "EUR")) // WORKS
When observing the value:
TextField("0", value: Binding(get: {
entryPrice
}, set: { newValue in
print(newValue)
entryPrice = newValue
}), format: .currency(code: "USD"))
I notice while you are entering decimal numbers the entry price gets updated. But whenever the focus leaves the textfield on USD or other locale than EUR it retrieves nil as the newValue and the TextField becomes empty. It is really frustrating, because I don't know why this happens.
xCode 14.1 Beta 2
iOS 16.0
Found a Workaround
TextField("0", value: $entryPrice, format: .currency(code:"USD").locale(Locale(identifier: "en_US")))

Related

swiftui with mixed picker

I'm trying to make a mixed picker in swift ui. Means: first part is made of values from a constant, second part is made of core data entity values.
My Code so far
Structs
struct StandardArten: Identifiable, Hashable {
var id: UUID = UUID()
var name: String
}
struct Constants {
static let standardKontakte = [
StandardArten(name: "Hausarzt")
]
}
picker
Picker("Kategorie", selection: $kategorie) {
Text("Bitte wählen...").tag(Optional<UUID>(nil))
ForEach(Constants.standardKontakte, id: \.id) { kat in
Text(kat.name).tag(kat.id)
}
Divider()
ForEach(adressKategorien, id:\.id) { kat in
Text(kat.name ?? "ohne Titel").tag(kat.id)
}
}
The picker is displayed properly (see image), but selecting "Hausarzt" the picker "jumps back" to the old value.
So what am I doing wrong here?
Additional information:
$kategorie is declared as #State var kategorie: UUID?

Picker choice changing the TextField in SwiftUI

I want the user to be able to choose the currency they want to put in my app. But with this code:
import SwiftUI
struct ContentView: View {
#State private var amount = 0.0
#State private var currency = "USD"
let currencies = ["PLN", "USD", "EUR", "GBP", "JPY"]
var body: some View {
Form {
Picker("Currency", selection: $currency) {
ForEach(currencies, id: \.self) {
Text($0)
}
}
TextField("Amount", value: $amount, format: .currency(code: currency))
.keyboardType(.decimalPad)
}
}
}
The currency choices made with Picker don't change the currency type in the TextField (it stays USD at all times, even after deletion). How to push the chosen currency code to the TextField?
Format is not tracked to update TextField, you can do it forcefully using id depending on format parameter, like
TextField("Amount", value: $amount, format: .currency(code: currency))
.keyboardType(.decimalPad)
.id(currency) // << here !!
Tested with Xcode 13.2 / iOS 15.2

Observing changes #FocusState variable not working as expected

I want to validate the user's changes in a specific textfield and thus need to hook in when this textfield loses focus. I am using this year's introduced .focused(_:equals:) modifier with an enum and a #FocusState.
Observing my optional #FocusState var, which is nil when the screen first loads, with .onChange(of:perform:), this is only called once, when #FocusState changes from nil to a value–but not when it changes to another enum value.
The latter is expected though, and once this happens I could check what the previous field was–at least that's my approach for now.
Why is this approach not working–and is there a better way to go about this?
struct FocusView: View {
#State private var nameLast = "Ispum"
#State private var phoneNumber = "+41 79 888 88 88"
enum Field {
case nameLast
case phoneNumber
}
#FocusState private var focusedField: Field?
#State private var prevFocusedField: Field?
var body: some View {
VStack {
TextField("Last Name", text: $nameLast)
.focused($focusedField, equals: .nameLast)
TextField("Phone Number", text: $phoneNumber, prompt: Text(""))
.focused($focusedField, equals: .nameLast)
}
.onChange(of: focusedField) { newFocusField in
//only called once–when `focusedField` changes its state from `nil` to a value
print("Old value: \(prevFocusedField)")
print("New value: \(focusedField)")
if prevFocusedField == .phoneNumber {
//Validation
}
prevFocusedField = newFocusField
}
.textFieldStyle(.roundedBorder)
.padding()
}
}
There is a typo in your code (probably copy-paste)
TextField("Phone Number", text: $phoneNumber, prompt: Text(""))
.focused($focusedField, equals: .nameLast) // << here !!
you should use different value for second field:
VStack {
TextField("Last Name", text: $nameLast)
.focused($focusedField, equals: .nameLast)
TextField("Phone Number", text: $phoneNumber, prompt: Text(""))
.focused($focusedField, equals: .phoneNumber) // << fix !!
}
Tested with Xcode 13 / iOS 15

swiftui Textfield moves cursor to end with each change

If I pass a binding to the TextField then whenever you edit the centre of the text the cursor will jump to the end of the line after each character insert.
This only occurs on an actual device (iPadOS). The simulator does not display this behaviour.
My work around is to create a state variable that I set onAppear with the value from the binding and copy onEditingChanged. I then pass this state variable to the TextField instead of the binding variable directly. This breaks the one source of truth.
Does anyone have a better solution.
Swift 5
iOS 14.7
XCODE 13 Beta Aug 10
struct infoSheetView: View {
#Binding var cameraURL: String
#State var tmpCameraURL: String = ""
var body: some View {
VStack {
Form{
Section(header: Text("Camera").font(.title)) {
TextField("Camera URL", text: $tmpCameraURL, onEditingChanged: {_ in
cameraURL = tmpCameraURL
}).autocapitalization(.none).disableAutocorrection(true)
}
}.frame(width:400,height:390)
.onAppear() {
tmpCameraURL = cameraURL
}
}
}

SwiftUI TextField binding to Double not working with custom Formatter

I have a Binding to a Double parameter that is set by a SwiftUI TextField. I use a custom Formatter that converts to and from a Double value. The TextField sends an empty string "" to the Formatter upon editing so the conversion fails and the Double parameter is not updated. The struct is called from a parent View which has a #ObjectBinding parameter and the Double is a parameter of that object.
I am currently using Xcode 11 beta 3 and macOS Catalina Beta 3. The TextField works if the parameter is a String. The problem appears to be that a non-String type, which requires a Formatter fails to properly update the #Binding value.
Here is the Formatter:
public class DoubleFormatter: Formatter {
override public func string(for obj: Any?) -> String? {
var retVal: String?
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
if let dbl = obj as? Double {
retVal = formatter.string(from: NSNumber(value: dbl))
} else {
retVal = nil
}
return retVal
}
override public func getObjectValue(_ obj: AutoreleasingUnsafeMutablePointer<AnyObject?>?, for string: String, errorDescription error: AutoreleasingUnsafeMutablePointer<NSString?>?) -> Bool {
var retVal = true
if let dbl = Double(string), let objok = obj {
objok.pointee = dbl as AnyObject?
retVal = true
} else {
retVal = false
}
return retVal
}
}
Here is the SwiftUI View that takes the Double parameter in a TextField
struct HStackTextTextField : View {
var text: String
#Binding var value: Double
#State var valueState: Double
var body: some View {
HStack {
Text("\(value)") //shows the value failing to update
TextField("Number", value: $value, formatter: DoubleFormatter()) //Still Fails
Text("\(valueState)") //shows valueState updating properly
TextField("Number", value: $valueState, formatter: DoubleFormatter()) //works as expected
}
}
}
I expect the TextField value to update when I type, but it does not. When I trace the value in the the Formatter. The string provided to getObjectValue is "" instead of the value in the TextField.
UPDATE: As of catalina/Xcode beta 5, this still appears to be an issue when the View TextField parameter is defined as #Binding and passed to the View. It appears to work as expected if the TextField parameter is defined as #State and is local to the View.
I believe this is a bug in SwiftUI. (See my similar question: SwiftUI TextField with formatter not working?)
In beta 2, it didn't work at all. In beta 3, I think you'll find that your result gets passed to your formatter if (and only if) you hit return after typing in the field. Hopefully in beta 4 they'll finish fixing the bug!
SwiftUI 3.0 still facing the same issue when using custom formatter in TextField. In my case I was using number formatter with 2 fractions. Here is my workgaround I found which looks legit and works correctly. I used custom binding.
#Binding var amount: Double?
var body: some View {
VStack {
let amountBinding = Binding(
get: { self.amount ?? 0.0 },
set: { self.amount = Double(String(format:"%.2f", $0))})
TextField("€", value: amountBinding, formatter: NumberFormatters.twoFractionDigits, onEditingChanged: { changed in
// Editing changed
})
Spacer()
}
}
struct NumberFormatters {
static var twoFractionDigits: Formatter {
let formatter = NumberFormatter()
formatter.numberStyle = .none
formatter.maximumFractionDigits = 2
return formatter
}
}