9

Im trying to implement a view that can change the amount of displaying items (created by a ForEach loop) if the content array's size changes, just like how a shopping app might change its number of items available after the user pull to refresh

Here are some code I have tried so far. If I remember correctly, these worked with Xcode beta 4, but with beta 5:

  • If the array's size increases, the loop will still display the originial number of elements
  • Array's size decrease will cause an index out of range error

Code:

import SwiftUI

struct test : View {
    @State var array:[String] = []
    @State var label = "not pressed"
    var body: some View {
        VStack{
            Text(label).onTapGesture {
                self.array.append("ForEach refreshed")
                self.label = "pressed"
            }
            ForEach(0..<array.count){number in
                Text(self.array[number])
            }
        }
}
}

#if DEBUG
struct test_Previews: PreviewProvider {
    static var previews: some View {
        test()
    }
}
#endif

I'm new to SwiftUI and GUI programming in general, and just feels like every content is defined at launch time and really hard to make changes afterwards (For example: reset a view after the user navigate away then return to it). Solutions to the loop problem or any tips for making views more dynamic would be greatly appreciated!

Nguyễn Khắc Hào
  • 1,980
  • 2
  • 15
  • 25

1 Answers1

8

Beta 5 Release Notes say:

The retroactive conformance of Int to the Identifiable protocol is removed. Change any code that relies on this conformance to pass .self to the id parameter of the relevant initializer. Constant ranges of Int continue to be accepted:

List(0..<5) {
   Text("Rooms")
}

However, you shouldn’t pass a range that changes at runtime. If you use a variable that changes at runtime to define the range, the list displays views according to the initial range and ignores any subsequent updates to the range.

You should change your ForEach to receive an array, instead of range. Ideally an Identifiable array, to avoid using \.self. But depending on your goal, this can still work:

import SwiftUI

struct ContentView : View {
    @State var array:[String] = []
    @State var label = "not pressed"
    var body: some View {
        VStack{
            Text(label).onTapGesture {
                self.array.append("ForEach refreshed")
                self.label = "pressed"
            }
            ForEach(array, id: \.self) { item in
                Text(item)
            }
        }
    }
}

Or as rob mayoff suggested, if you need the index:

struct ContentView : View {
    @State var array:[String] = []
    @State var label = "not pressed"
    var body: some View {
        VStack{
            Text(label).onTapGesture {
                self.array.append("ForEach refreshed")
                self.label = "pressed"
            }
            ForEach(array.indices, id: \.self) { index in
                Text(self.array[index])
            }
        }
    }
}```
kontiki
  • 37,663
  • 13
  • 111
  • 125