Memory caching Views

I am fairly new to SwiftUI and trying to build an app with multiple WKWebViews that I can loop through with a touch of a button. I have been failing to switch between WebViewRepresentables (WKWebKit wrapper). Since wrapping UIKit and handling all the delegates is a complex thing to do, I thought there is more room for errors and decided to create another project with only colours being looped through.

I am trying to go through ColorViews in the code snippet below.

I am expecting to see This is after 3 seconds. message 3 seconds after I get to them (onAppear{}). However, once it runs for the first view, it also runs for all other views. The other two get to have the text there too when I go through with the button.

Why does onAppear code block run only once (breakpoint hits only once) and the result applies to each view even though they are all different instances of ColorView?

I am expecting each view having a random number generated only once, but in the code snippet below I am observing that a fresh random number being generated every time I switch to next view. I thought storing them in-memory array would prevent that as they would be instanced once?

What is the correct way of memory caching views so that they can be switched between (each being initialised only once and not being refreshed each time shown)?

struct ColorView: View {
    @State private var text = ""
    let color: Color
    
    var body: some View {
        VStack {
            Text(text)
            Text("\(Int.random(in: 1..<10))")
            color
        }
        .onAppear {
            DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
                self.text = "This is after 3 seconds."
            }
        }
    }
}

struct ContentView: View {
    @State private var colors: [ColorView] = [ColorView(color: .red), ColorView(color: .yellow), ColorView(color: .gray)]
    @State private var index = 0
    
    var body: some View {
        VStack {
            Text("page \(index)")
            Button(action: { index += 1; index = index % 3 }, label: { Text("Go through") })
            colors[index]
        }
    }
}

1 answer

  • answered 2021-10-24 17:54 Simone Pistecchia

    Take into account the Asperi comment, but if you want to force the job, following this to understand how SwiftUI is working: This appen because the view is loaded at once first time and it's changhing only if someting inside is changing.

    If you add .id() the view is refreshing if the id is changing.

    struct ContentView: View { @State private var colors: [ColorView] = [ColorView(color: .red), ColorView(color: .yellow), ColorView(color: .gray)] @State private var index = 0

    var body: some View {
        VStack {
            Text("page \(index)")
            Button(action: { index += 1; index = index % 3 }, label: { Text("Go through") })
            Divider()
            colors[index]
                .id(index) //--> add this
        }
    }
    

How many English words
do you know?
Test your English vocabulary size, and measure
how many words do you know
Online Test
Powered by Examplum