Conditional Bindings in SwiftUI
Perhaps you have a component that has a Binding to a state. So that component, when given a binding, can alter the state, but does not own that state; it just has a Binding to it.
It does not own that state because the owner of that state might have even more plans for that state variable, given the context it finds itself in.
So there might be times where the component that is Binding that state should not change the state. A very specific use case is a segmented control. Perhaps you tap to change the segment of the segmented control, but due to some other state, like the data to display is not yet available, you might want to disallow that.
I present: the conditional Binding, and it looks like this:
extension Binding
{
/// A means to introduce conditional logic into a binding as to whether it should change or not.
func onChange(
willSet willSetHandler: ((Value, Value) -> Bool)?,
didSet didSetHandler: ((Value, Value) -> Void)?
) -> Binding<Value> {
Binding(
get: { self.wrappedValue },
set: { newValue in
let oldValue = self.wrappedValue
let shouldChange = willSetHandler?(oldValue, newValue) ?? true
if shouldChange {
self.wrappedValue = newValue
didSetHandler?(oldValue, newValue)
}
}
)
}
}
You can see above that this enables a few things:
- You have callbacks for
willSet
anddidSet
, which would not work for a@State var
- You can, via the return value in the
willSetHandler
if the value should change at all.
Then in your view builder method, you would have something like:
MySelectionControl(
selectedIndex: $selectedIndex.onChange(
willSet: {
/* Stuff that determines whether to return true or false */
return true // or false!
},
didSet: nil
)
)
Handy little trick I'd say!