Reputation: 81
I'm trying to implement some TextFields that accept any number in a desired range. This is, if the user is entering an value, I'd like it to be from min to max dynamically , for example. However, I don't know how to control this in a TextField.
struct Container {
var textInput: Double
}
struct ContentView: View {
@State private var container = Container
var body: some View {
TextField("", value: $container.textInput, format: .number)
.keyboardType(.decimalPad)
.frame(width: 200, height: 20)
.padding()
}
}
Upvotes: 1
Views: 2096
Reputation: 406
Had the exact same problem and came up with this:
Using a custom formatter – it’s not perfect but it works the way I want it to.
class BoundFormatter: Formatter {
var max: Int = 0
var min: Int = 0
func clamp(with value: Int, min: Int, max: Int) -> Int{
guard value <= max else {
return max
}
guard value >= min else {
return min
}
return value
}
func setMax(_ max: Int) {
self.max = max
}
func setMin(_ min: Int) {
self.min = min
}
override func string(for obj: Any?) -> String? {
guard let number = obj as? Int else {
return nil
}
return String(number)
}
override func getObjectValue(_ obj: AutoreleasingUnsafeMutablePointer<AnyObject?>?, for string: String, errorDescription error: AutoreleasingUnsafeMutablePointer<NSString?>?) -> Bool {
guard let number = Int(string) else {
return false
}
obj?.pointee = clamp(with: number, min: self.min, max: self.max) as AnyObject
return true
}
}
Then I use it like this:
let max: Int = 100
let min: Int = 0
var formatter: BoundFormatter {
let formatter = BoundFormatter()
formatter.setMax(self.max)
formatter.setMin(self.min)
return formatter
}
@Binding var value: Int = 0
//// VIEW BODY \\\\
TextField("Number here:", value: $value, formatter: boundFormatter)
You can even improve this version by setting min max in the formatter as bindings, so you have dynamic bounds.
class BoundFormatter: Formatter {
@Binding var max: Int
@Binding var min: Int
// you have to add initializers
init(min: Binding<Int>, max: Binding<Int>) {
self._min = min
self._max = max
super.init()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented"
}
...
}
/// usage
TextField("Number here:", value: $value, formatter: BoundFormatter(min: .constant(0), max: $max))
Upvotes: 1
Reputation: 1
TextField(
.onChange(of: text, perform: {
text = String($0.prefix(1))
})
Upvotes: 0