Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Why does one child view in SwiftUI re-render on parent state change but another doesn't?

The background color of SecondView doesn’t change as expected when I push the button, however, the random number in ThirdView updates each time. Why does this happen, both secondView and thirdView have no explicit dependency on the state, I think both of them should not be re-created.

Apple Docs: When the @State value changes, SwiftUI updates the parts of the view hierarchy that depend on the value.

struct ParentView: View {
    @State var counter: Int = 0

    var body: some View {
        VStack {
            Text("ContentView \(counter)")
            SecondView()
            ThirdView()
            Button {
                counter += 1
            } label: {
                Text("Go!")
            }
        }
    }
}

struct SecondView: View {
    var body: some View {
        Text("SecondView")
            .background(.debug)
    }
}

struct ThirdView: View {
  let randomNumber = Int.random(in: 1...100)
  var body: some View {
    Text("Random Number: \(randomNumber)")
  }
}


public extension ShapeStyle where Self == Color {
    static var debug: Color {
        Color(
            red: .random(in: 0...1),
            green: .random(in: 0...1),
            blue: .random(in: 0...1)
        )
    }
}

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

>Solution :

If you delete the Text("ContentView \(counter)"), ThirdView will stop updating.

Do you see now why ThirdView updates? You are requiring the whole VStack to regenerate, so SecondView and ThirdView are candidates to be instantiated all over again; these would be entirely new views, and so the new instance of ThirdView gets a new random number as its randomNumber property. (randomNumber could not possibly change; it is a let constant!)

As for SecondView, its color comes from a computed variable. It doesn’t care what happens to ParentView. So SecondView doesn’t need regeneration, and the background color of SecondView remains unchanged — until you run the entire app again.

To make SecondView act like ThirdView, give it a let property that is a random color, just like ThirdView:

struct SecondView: View {
    let debug = Color(
        red: .random(in: 0...1),
        green: .random(in: 0...1),
        blue: .random(in: 0...1)
    )
    var body: some View {
        Text("SecondView")
            .background(debug)
    }
}

Or, play the game the other way: replace let in ThirdView with @State var. Now ThirdView no longer updates when you tap the button.

struct ThirdView: View {
    @State var randomNumber = Int.random(in: 1...100)
    var body: some View {
        Text("Random Number: \(randomNumber)")
    }
}
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading