onPreferenceChange not called when Parent View has a Date Picker, SwiftUI

Viewed 740

I have one parent View which has two Child views.

When the child view changes the preference value, the parent receives the preference changes via onPreferenceChange However, when the parent has a Date Picker, onPreferenceChange doesn't get called and stops working. Has anyone found any workaround for this ?

struct parentView: View {

@State private var date = Date()

var body: some View {
    
    VStack{
        
        ChildView1()
        
        Text("Child View 2")
    
       // DatePicker("", selection: self.$date, displayedComponents: .hourAndMinute)
       

    }
    .onPreferenceChange(testPreference.self, perform: { value in
        print("Printing preference value")
        print(value)
    })
 }
}


struct ChildView1: View {

@State private var tablets : Int = 0

var body: some View {
    Text("Child View 1")
        .onTapGesture {
            self.tablets = self.tablets + 1
        }
        .preference(key: testPreference.self, value: self.tablets)

  }
}


struct testPreference: PreferenceKey {

typealias Value = Int

static var defaultValue: Int = 0

static func reduce(value: inout Int, nextValue: () -> Int) {
    value = nextValue()
  }
}
1 Answers

I also got stuck on the same problem and Apple told me this was expected. Our fault is we implemented this reduce() incorrectly:

static func reduce(value: inout Int, nextValue: () -> Int) {
    value = nextValue()
}

This overwrites values explicitly written by views, by the default value written by every view. Therefore, you need to rewrite that function so that applying it to the default value has no effect, as described in the documentation of PreferenceKey.defaultValue:

Views that have no explicit value for the key produce this default value. Combining child views may remove an implicit value produced by using the default. This means that reduce(value: &x, nextValue: {defaultValue}) shouldn’t change the meaning of x.

So you'd need to replace this line:

value = nextValue()

with this:

value = max(value, nextValue())

or this:

value += nextValue()

or something like that.

Related