I have the following SwiftUI View
(extracted & simplified code):
struct MyView<T : Hashable>: View {
private let selection: Binding<T?>?
init(selection: Binding<T?>) {
self.selection = selection
}
init() {
self.selection = nil
}
var body: some View {
VStack {
...
}
.onChange(of: selection) { newValue in <== ERROR
...
}
}
}
I get the following error: Referencing instance method 'onChange(of:perform:)' on 'Optional' requires that 'Binding<T?>' conform to 'Equatable'
.
How could this be resolved?
CodePudding user response:
Switch to use the wrapper
struct MyView<T : Hashable>: View {
@Binding var selection: T?
init(selection: Binding<T?>) {
self._selection = selection
}
init() {
//Dead end not recommended for use outside of Previews
//Will cause undesirable behavior by mimicking having a source of truth.
self._selection = .constant(nil)
}
var body: some View {
VStack {
Text("")
}
.onChange(of: selection) { newValue in //<== ERROR
//Do work here
}
}
}
https://developer.apple.com/documentation/swiftui/binding/constant(_:)
You should also note that Binding
is by definition a two-way connection.
Why have a Binding
if its changes will go to a dead end? You can get similar results with...
struct MyView<T : Hashable>: View {
let selection: T?
var body: some View {
VStack {
Text("")
}.onAppear(){
//Do work here
}
}
}
private let selection: Binding<T?>?
is "equivalent" (but not really) to using the wrapper and then accessing $selection
.
I have never dug into the reason why SwiftUI requires the wrappers vs just the types but it has to do with the DynamicProperty
conformance, wrappedValue
and projectedValue
. The View
will not know when to update if you use Binding<Type>
vs @Binding
.
The undesired behavior having views that will provide the user with a UX that hints that their actions are affecting something meaningful, plus there are other bugs just as jerky behavior and unresponsive UI.