I'm trying to pass value changes of a binding into a view modifier. Similar to some of the other view modifiers that are being used inside Apple, e.g.;
func presentationDetents(
_ detents: Set<PresentationDetent>,
selection: Binding<PresentationDetent>
) -> some View
But the view modifier never gets called when the binding changes. Whenever I use a plain value it works.
Here is a single file example of what I'm trying to do and what works and what does not work marked.
import SwiftUI
extension View {
func changeNumber(number: Int) -> some View {
print(number) // When running changeNumber with a plain value this does get called.
return self
}
func changeNumber(number: Binding<Int>) -> some View {
print("Binding", number) // Does not get called
return self
}
}
struct DebugView: View {
@State var number = 0
var body: some View {
VStack {
Button("Change Number") {
number = Int.random(in: 0..<50)
}
Text("Hello, World!")
.changeNumber(number: $number) // $number breaks, number works.
}
}
}
struct DebugView_Previews: PreviewProvider {
static var previews: some View {
DebugView()
}
}
Alright, with some pointers of @lorem ipsum, I now know what I did wrong. I solved it by using the wrapped value on the preference key and passing that up in the view chain. Final code that does work;
import SwiftUI
public struct NumberKey: PreferenceKey {
public static var defaultValue: Int = 0
public static func reduce(value: inout Int, nextValue: () -> Int) {
value = nextValue()
}
}
extension View {
func changeNumber(number: Int) -> some View {
print(number)
return self
}
func changeNumber(number: Binding<Int>) -> some View {
print("Binding", number.wrappedValue)
return self.preference(key: NumberKey.self, value: number.wrappedValue)
}
}
struct DebugView: View {
@State var number = 0
var body: some View {
VStack {
Button("Change Number") {
number = Int.random(in: 0..<50)
}
Text("Hello, World!")
.changeNumber(number: $number)
}
.onPreferenceChange(NumberKey.self) { value in
print("Changed", value)
}
}
}
struct DebugView_Previews: PreviewProvider {
static var previews: some View {
DebugView()
}
}