Search code examples
swiftxcodeviewswiftui

SwiftUI ForEach based on State int


I am storing a Int value as State in my View. When I press a button the Int increase by one. This is working fine when I print my int value.

I have now a ForEach loop, which iterates based on this Int. When I set my State on 2 by default it works fine at the beginning. However, when I increase that Int my ForEach is not called again.

I understand that State will reload my actual view. Does it only load specific parts?

Here I declare my State:

@State var s_countVenues   : Int = 2

This is the ForEach I use. It works at the beginning, however changing s_countVenues does NOT update the view.

ForEach(0..<self.s_countVenues)
{_ in
    HStack(spacing: 0)
    {
        //here comes my view
    }
}

If necessary, here I am increasing my value by one. It works, I printed the changes and if I use it inside a Label, the Label gets updated.

self.s_countVenues += 1

TL:DR:

My Int State is working. I can increase and print it inside a label. However, using it as Statement in ForEach does not call that loop again after changing.


Solution

  • from apple docs

    extension ForEach where Data == Range<Int>, ID == Int, Content : View {
    
        /// Creates an instance that computes views on demand over a *constant*
        /// range.
        ///
        /// This instance only reads the initial value of `data` and so it does not
        /// need to identify views across updates.
        ///
        /// To compute views on demand over a dynamic range use
        /// `ForEach(_:id:content:)`.
        public init(_ data: Range<Int>, @ViewBuilder content: @escaping (Int) -> Content)
    }
    

    So, you have to use (as suggested by Apple)

    struct ContentView: View {
        @State var counter = 0
        var body: some View {
            VStack {
                ForEach(0 ..< counter, id:\.self) { i in
                        Text("row: \(i.description)")
                    }
                Button(action: {
                    self.counter += 1
                }, label: {
                    Text("counter \(counter.description)")
                })
            }
        }
    }