0

I have this view, for example:

struct MyChartView: View {
    let chart: MyChart
    @State private var selectedPoint: Int? = nil

    var body: some View {
        //... draw chart, set/move selectedPoint by a gesture ...
    }
    .onChange(of: chart) { selectedPoint = nil } 
}

How to reset selectedPoint var when the view is reinitialized with new chart and before the render pass happens? onChange triggers after the first render pass and it causes inconsistency, since chart already contains new value that was passed to init(). I couldn't find any by-design approaches, and from what I've tried only this hack works:

Introduce a state var for chart hash and compare it each time with new hash in render pass. If it doesn't match, skip the render:

    @State private var chartHash: Int
    
    var body: some View {
        let newChartHash = computeChartsHash(chart)
        if newChartHash != self.chartHash {
            return
        }
        ...
6
  • What "inconsistency" does onChange cause? This sounds like an XY Problem. You could use the chart itself as the id of the view, and that would destroy and recreate the whole view, including its state. You could also change selectedPoint to include information about which chart it is for, so that an "invalid" selection can be easily detected, which is similar to your approach using a hash. Commented Sep 30 at 0:44
  • So you kind of already answered your own question. What kind of answer are you looking for? Commented Sep 30 at 0:45
  • @Sweeper my own answer of skipping the render pass is just a hacky workaround. The correct solution would be to get selectedPoint reset before the very first render pass with new chart value. I just tried your idea with view id - so far the same result. The other option to include more info in selectedPoint looks good... Commented Sep 30 at 1:55
  • 1
    Including more info in selectedPoint doesn't "get selectedPoint reset before the very first render pass" (your definition of a "correct solution") though, but you say it "looks good". In any case, I believe you should be fixing the "inconsistency", and not worry about render passes. That is, make body still produce a "consistent" view even if selectedPoint is invalid. You would check if selectedPoint is valid and if it is not, treat it as nil. Without further details though, I can't tell you exactly how to implement this. Commented Sep 30 at 2:03
  • 1
    I think it’s too late to do it here. Better to reset selection in a handler that’s called when the chart is loaded. You could have a custom state struct that has both a copy of the chart and a selection. Commented Sep 30 at 7:29

1 Answer 1

0

You can set the identity of the view. In the view above this (Parent View)...

MyChartView(chart: selectedChart)
   .id(selectedChart) //This line

When selectedChart changes the entire view will be recreated, including the State.

This adds a level of inefficiency since SwiftUI isn't selecting what to redraw/recreate.

But likely necessary since the entire view changes when the chart changes.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.