Skip to content

Instantly share code, notes, and snippets.

@ccwasden
Last active March 15, 2021 18:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ccwasden/fc297ba13d49e40690c597aba9b351e6 to your computer and use it in GitHub Desktop.
Save ccwasden/fc297ba13d49e40690c597aba9b351e6 to your computer and use it in GitHub Desktop.
struct NumberField<T: Numeric & LosslessStringConvertible>: View {
let prompt: String
@Binding var value: T?
@StateObject var viewModel = ViewModel()
class ViewModel: ObservableObject {
@Published var text = ""
var ignoreNextChange = false
}
var body: some View {
TextField(prompt, text: text)
.onAppear(perform: {
viewModel.text = value.map(String.init) ?? ""
})
.onChange(of: self.value, perform: { value in
if let v = value, !self.viewModel.ignoreNextChange {
self.viewModel.text = String(v)
}
self.viewModel.ignoreNextChange = false
})
}
var text: Binding<String> {
Binding {
viewModel.text
} set: { (value) in
let sanitized = sanitize(value: value)
self.viewModel.text = sanitized
viewModel.ignoreNextChange = true
self.value = T(sanitized)
}
}
func sanitize(value: String) -> String {
return value.trimmingCharacters(in: CharacterSet(charactersIn: "-.0123456789").inverted)
}
}
// Usage
struct ContentView: View {
@State var num: Float? = 0
var body: some View {
Text("VALUE \(num ?? 0)")
NumberField(prompt: "Enter number", value: $num)
Stepper("Thing", value: Binding(get: { num ?? 0 }, set: { num in
self.num = num
}))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment