SwiftUI не подозревает, что внутреннее состояние DoubleField View изменилось. В то время, когда вы устанавливаете _stringValue
, экземпляр DoubleField
все еще не существует, поэтому вы не можете написать stringValue = ... в init (..) Оболочка свойства состояния может сообщить SwiftUI только после инициализации, что его обернут значение было изменено, не раньше.
попробуйте следующее
import SwiftUI
struct ContentView: View {
@State var amount:Double = 300
var body: some View {
VStack {
Text("\(self.amount)")
DoubleField(value: $amount).id(amount)
Button(action: {
self.amount += 10
}) { Text("Submit") }
}
.padding()
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
struct DoubleField:View {
@Binding var doubleValue:Double
@State var stringValue:String
init(value: Binding<Double>) {
print("DoubleField.init(value: \(value.wrappedValue))")
self._doubleValue = value
self._stringValue = State(initialValue: "\(value.wrappedValue)")
print(stringValue)
}
var body: some View {
VStack {
Text(stringValue)
TextField("20.00", text: $stringValue)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
все поля обновляются по мере необходимости, поскольку SwiftUI видит изменение из-за модификатора .id
В вашем case просто используйте
DoubleField(value: $amount).id(amount)
Это работает, потому что Double соответствует Hashable и будет пересчитывать свое тело только при изменении значения количества.
UPDATE
Вы можете создать пользовательскую привязку, как я сделал в следующем примере. Это Binding with backend Double, заключенный в @State (для информирования SwiftUI о любом изменении его значения)
let binding = Binding<String>(get: {
if self.empty { return "" } else {
return self.format(value: self.value.value)
}
}) { (str) in
self.empty = str.isEmpty
// before string to double
// replace decimal separator to "." !!!
let dot = Locale.current.decimalSeparator ?? "."
let s = str.replacingOccurrences(of: dot, with: ".")
self.value.value = Double(s) ?? 0.0
}
где
func format(value: Double) -> String {
// for presentation
// replace "." with decimal separator
let dot = Locale.current.decimalSeparator ?? "."
var s = String(format: "%f", value)
.replacingOccurrences(of: ".", with: dot)
.reversed()
.drop(while: {$0 == "0"})
.reversed()
.map(String.init)
.joined()
if let last = s.last, String(last) == dot {
s.removeLast()
}
return s
}
Работает с TextField намного лучше, чем сборка Formatter -включает поддержку в TextField, а также поддерживает зависящий от локали десятичный разделитель
и пример использования 
использование
TextField ("0", текст : переплет)