I created a @propertyWrapper to limit the number a variable can reach. I tried it in a SwiftUI view with a button that increases the value of the variable and it works, the variable stops at the maximum number set in the initializer. However if I try it with a Textflied it doesn't work, if I insert a higher number than the one set nothing happens, it makes me do it. How can I solve this problem, I know the problem has to do with Binding but I don't know exactly what it is, here is the code:
import SwiftUI
struct ContentView: View {
@Maximum(maximum: 12) var quantity: Int
var body: some View {
NavigationView{
Form{
TextField("", value: $quantity, format: .number, prompt: Text("Pizza").foregroundColor(.red))
Button {
quantity = 1
} label: {
Text("\(quantity)")
}
}
}
}
}
@propertyWrapper
struct Maximum<T: Comparable> where T: Numeric {
@State private var number: T = 0
var max: T
var wrappedValue: T {
get { number }
nonmutating set { number = min(newValue, max) }
}
var projectedValue: Binding<T> {
Binding(
get: { wrappedValue },
set: { wrappedValue = $0 }
)
}
init(maximum: T){
max = maximum
}
}
extension Maximum: DynamicProperty {
}
CodePudding user response:
@Asperi is exactly right as to how to create the property wrapper. However, that does not solve the problem of the TextField()
. The issue seems to be that You are not actually using $quantity
on your TextField
, but rather are using a string from the formatter that is derived from $quantity.
That just doesn't seem to allow the update mechanism to work properly.
However, you can fix this simply by feeding a @State
string into the TextField
, and then updating everything in an .onChange(of:)
. This allows you to set quantity
to the Int
value of the TextField
, and the maximum prevents the quantity
from going too high. You then turn around and set your string to the quantity.description
to keep everything in sync.
One last thing, I changed the keyboardType
to .decimalPad
to make inputting easier.
struct ContentView: View {
@Maximum(maximum: 12) var quantity: Int
@State private var qtyString = "0"
var body: some View {
NavigationView{
Form{
TextField("", text: $qtyString, prompt: Text("Pizza").foregroundColor(.red))
.onChange(of: qtyString) { newValue in
if let newInt = Int(newValue) {
quantity = newInt
qtyString = quantity.description
}
}
.keyboardType(.decimalPad)
Button {
quantity = 1
} label: {
Text("\(quantity)")
}
}
}
}
}
@propertyWrapper
struct Maximum<T: Comparable>: DynamicProperty where T: Numeric {
let number: State<T> = State(initialValue: 0)
var max: T
var wrappedValue: T {
get { number.wrappedValue }
nonmutating set { number.wrappedValue = min(newValue, max) }
}
var projectedValue: Binding<T> {
Binding(
get: { wrappedValue },
set: { wrappedValue = $0 }
)
}
init(maximum: T){
max = maximum
}
}