Why isn't onPreferenceChange being called if it's inside a ScrollView in SwiftUI?

why isn't my brain working supplements
why isn't my brain working pdf
why isn't my brain working audiobook
why isn't my brain working review
why isn't my brain working pdf download
why isn't my brain working reddit
kharrazian brain book
is netflix down or is it just me

I've been seeing some strange behavior for preference keys with ScrollView. If I put the onPreferenceChange inside the ScrollView it won't be called, but if I put it outside it does!


I've setup a width preference key as follows:

struct WidthPreferenceKey: PreferenceKey {
    typealias Value = CGFloat
    static var defaultValue = CGFloat(0)

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

The following simple view does not print:

struct ContentView: View {
    var body: some View {
        ScrollView {
            Text("Hello")
                .preference(key: WidthPreferenceKey.self, value: 20)
                .onPreferenceChange(WidthPreferenceKey.self) {
                    print($0) // Not being called, we're in a scroll view.
                }
        }
    }
}

But this works:

struct ContentView: View {
    var body: some View {
        ScrollView {
            Text("Hello")
                .preference(key: WidthPreferenceKey.self, value: 20)
        }
        .onPreferenceChange(WidthPreferenceKey.self) {
            print($0)
        }
    }
}

I know that I can use the latter approach to fix this, but sometimes I'm inside a child view that does not have access to its parent scroll view but I still want to record a preference key.

Any ideas on how to get onPreferenceChange to get called inside a ScrollView?

Note: I get Bound preference WidthPreferenceKey tried to update multiple times per frame. when I put the function inside the scroll view, which might explain what is going on but I can't figure it out.

Thanks!

You may only read it in superView, but you can change it with transformPreference after you set it .

struct ContentView: View {
var body: some View {
    ScrollView {
        VStack{
        Text("Hello")
            .preference(key: WidthPreferenceKey.self, value: 20)
        }.transformPreference(WidthPreferenceKey.self, {
        $0 = 30})
    }.onPreferenceChange(WidthPreferenceKey.self) {
        print($0)
    }
}
}

The last value is 30 now. Hope it is what you want.

You can read from other layer:

  ScrollView {

        Text("Hello").preference(key: WidthPreferenceKey.self, value: CGFloat(40.0))
            .backgroundPreferenceValue(WidthPreferenceKey.self) { x -> Color in
               print(x)
                return Color.clear
        }

    }

Why isn't FirstEnergy charged (yet) in the Ohio nuclear bailout , FirstEnergy Corp. appears to be connected to the $60 million bribery case involving Ohio House Speaker Larry Householder, but prosecutors� Outsourcing work to third-party vendors introduces risks that need to be managed. ISN provides contractor management software, ISNetworld, to help with the supplier management process including contractor safety and risk management.

I think onPreferenceChange in your example is not called because it’s function is profoundly different from preference(key…)

preference(key:..) sets a preference value for the view it is used on. whereas onPreferenceChange is a function called on a parent view – a view on a higher position in the view tree hierarchy. Its function is to go through all its children and sub-children and collect their preference(key:) values. When it found one it will use the reduce function from the PreferenceKey on this new value and all the already collected values. Once it has all the values collected and reduced them it will execute the onPreference closure on the result.

In your first example this closure is never called because the Text("Hello") view has no children which set the preference key value (in fact the view has no children at all). In your second example the Scroll view has a child which sets its preference value (the Text view).

All this does not explain the multiple times per frame error – which is most likely unrelated.

Recent update (24.4.2020): In a similar case I could induce the call of onPreferenceChange by changing the Equatable condition for the PreferenceData. PreferenceData needs to be Equatable (probably to detect a change in them). However, the Anchor type by itself is not equatable any longer. To extract the values enclosed in an Anchor type a GeometryProxy is required. You get a GeometryProxy via a GeometryReader. For not disturbing the design of views by enclosing some of them into a GeometryReader I generated one in the equatable function of the PreferenceData struct:

struct ParagraphSizeData: Equatable {
  let paragraphRect: Anchor<CGRect>?

  static func == (value1: ParagraphSizeData, value2: ParagraphSizeData) -> Bool {

    var theResult : Bool = false
    let _ = GeometryReader { geometry in
       generateView(geometry:geometry, equality:&theResult)
    }

    func generateView(geometry: GeometryProxy, equality: inout Bool) -> Rectangle {
       let paragraphSize1, paragraphSize2: NSSize

      if let anAnchor = value1.paragraphRect { paragraphSize1 = geometry[anAnchor].size }
       else {paragraphSize1 = NSZeroSize }
       if let anAnchor = value2.paragraphRect { paragraphSize2 = geometry[anAnchor].size }
       else {paragraphSize2 = NSZeroSize }

       equality = (paragraphSize1 == paragraphSize2)
       return Rectangle()
    }

   return theResult     
  }

}

With kind regards

Why Isn't Netflix Working, Why isn't Netflix working? If Netflix isn't working, you may be experiencing a network connectivity issue, an issue with your device, or an issue� On Sunday, Virginia Democratic Gov. Ralph Northam signed into law a measure that, among other things, makes Election Day a state holiday in the commonwealth.

I had been trying figure out this issue for a long time and have found how to deal with it although the way I used was just the one of workarounds.

Use onAppear to ScrollView with a flag to make its children show up.

...
@State var isShowingContent = false
...

ScrollView {
    if isShowingContent {
        ContentView()
    }
}
.onAppear {
     self.isShowingContent = true
}

Or,

Use List instead of it. It has the scroll feature and you can customize it with its own functionality and UITableView appearance in terms of UI. the most important is that it works as we expected.

[If you have time to read more] Let me say my thought about that issue.

I have confirmed that onPreferenceChange isn't called at the bootstrap time of a view which is put inside a ScrollView. I'm not sure if it is right behavior or not. But, I assume that it's wrong because ScrollView have to be capable to contain any views even if some of those use PreferenceKey to pass any data among views inside it. If it's right behavior, it would be quite easy of us to get in trouble when we try to create our custom views.

Let's get into more detail. I suppose that ScrollView would work slightly different from the other container views such as List, (H/V)Stack when it comes to setup its child view at the bootstrap time. In other words, ScrollView would try to draw(or lay out) children in its own way. Unfortunately, that way would affect the children's layout mechanism working incorrectly as what we've been seeing. We could guess what happened with the following message on debug view.

TestHPreferenceKey tried to update multiple times per frame.

It might be an evidence to tell us that the update of children has occurred while ScrollView is doing something for its setup. At that moment, it could be guessed that the update to PreferenceKey has been ignored.

That's why I tried to put the placing child views off to onAppear.

I hope that will be useful for someone who's struggling with various issue on SwiftUI.

Why isn't my app working?, Why isn't my app working? If you're having issues with your device or app, try these troubleshooting tricks: Try force-closing the app and reopening. Make sure � Why Isn't My Printer Printing? 6 printing issues you can fix. by. Tom Nelson. Writer. Tom Nelson is an engineer, programmer, network manager, and computer network and

It seems like the issue is not necessarily with ScrollView, but with your usage of PreferenceKey. For instance, here is a sample struct in which a PreferenceKey is set according to the width of a Rectangle, and then printed using .onPreferenceChange(), all inside of a ScrollView. As you drag the Slider to change the width, the key is updated and the print closure is executed.

struct ContentView: View {
    @State private var width: CGFloat = 100

    var body: some View {
        VStack {
            Slider(value: $width, in: 100...200)

            ScrollView(.vertical) {
                Rectangle()
                    .background(WidthPreferenceKeyReader())
                    .onPreferenceChange(WidthPreferenceKey.self) {
                        print($0)
                }
            }
            .frame(width: self.width)
        }
    }
}

struct WidthPreferenceKeyReader: View {
    var body: some View {
        GeometryReader { geometry in
            Rectangle()
                .fill(Color.clear)
                .preference(key: WidthPreferenceKey.self, value: geometry.size.width)
        }
    }
}

As you noted, the first time the key tries to set, the console prints "Bound preference WidthPreferenceKey tried to update multiple times per frame," but a real value is immediately set afterward, and it continues to update dynamically.

What value are you actually trying to set, and what are you trying to do in .onPreferenceChange()?

Why Isn't The Media Interviewing Portland's Rioters?, In the past, the news media has allowed activists engaged in violence a platform to speak, why aren't they doing so now? But that isn’t true for every interesting set of numbers. As an example, let’s look at the set of numbers of the form a + b √-5, or a +i b √5, where a and b are both integers and i is the

Why Isn't the 'Southern Strategy' Working?, Good morning. There are long lines for coronavirus tests. Tech companies are pulling back from Hong Kong. And President Trump's racial� Why isn't Netflix working? If Netflix isn't working, you may be experiencing a network connectivity issue, an issue with your device, or an issue with your Netflix

Why Isn't Borderline Personality Disorder Treated Like This Illness , Watch the rest of this series instantly HERE: https://bit.ly/2ZATzcZ What if we treated borderline Duration: 4:31 Posted: 5 days ago US Congress has authorized several trillion dollars in Covid-fighting stimulus programs. So what's holding the dollar up?I

Why isn't the Netherlands underwater? - Stefan Al, Dig into the incredible engineering of the Netherlands' Delta Works— the most sophisticated Duration: 5:24 Posted: Mar 24, 2020 Why Your New Hard Drive Isn’t Showing Up in Windows (and How to Fix It) Jason Fitzpatrick @jasonfitzpatric Updated July 3, 2017, 4:24pm EDT.

Comments
  • Looks like an issue of ScrollView, because replacing it with any other container, even List, in the provided example works perfectly.
  • What version of xcode were you on? I had working code using preference keys inside a scroll view in xcode 11.1 when I updated to 11.3 this morning it broke, with this error :/
  • Thanks. Although this works, as I said in the question I don't have access to the parent scroll view and I need a solution to read the value from inside the scroll view.
  • You can hook on preference on any outer super view,
  • I can't hook onPreferenceChange to any outer super view. Say the scroll view is given to you, i.e. something like var body: some View { ScrollView { MyView() } }. You only have access to MyView to play around with. Even if you add a VStack in MyView it won't work.
  • You can read from overlay or background. Although I don't know what is your purpose.
  • I have a similar situation and what you describe does not seem to be the case. Values are actually "collected", it's just that onPreferenceChange is not called with those values. Furthermore, when I start scrolling the view, onPreferenceChange is called, but not before that.
  • It also happens with a ZStack (which gets firstly sized by a Color.clear element in it, though making the ZStack as big as its container). Therefore I am not quite sure your reasoning about the ScrollView makes so much sense.
  • I'm trying to get the width of the smallest child and set the parent view to that width. I'll ask it as a new question
  • FYI, It occurs when you try to update the width at the startup time. not update it by input events like the slider.