Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI Looping through Indices

Tags:

ios

swiftui

I have the following code:

struct ContentView: View {
    
    let numbers = [1,2,3,4,5,6,1]
    
    var body: some View {
        
        List(numbers.indices) { index in
            let number = numbers[index]
            Text("\(number)")
        }
    }
}

I get the following warning:

Non-constant range: not an integer range

I can fix it using the following code:

 struct ContentView: View {
        
        let numbers = [1,2,3,4,5,6,1]
        
        var body: some View {
            
            List(numbers.indices, id: \.self) { index in
                let number = numbers[index]
                Text("\(number)")
            }
        }
    }

But I still don't understand what Non-constant range: not an integer range

like image 652
Mary Doe Avatar asked Aug 31 '25 01:08

Mary Doe


1 Answers

The List View uses ForEach under the hood.

If you create your list with the 'data' only constructor the constructor for ForEach has the requirment for its data to be constant not dynamic:

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)
}

If you change your constructor to provide the id of each element yourself List uses the recommended ForEach(_:id:content:) constructor.

It needs the id specified in order to know when new elements have been added.

/// To compute views on demand over a dynamic range use ForEach(_:id:content:).

Update:

dynamic means you cann add or remove new items from your array.

For example:

List((0..<4)) { index in
    let number = numbers[index]
    Text("\(number)")
}

the range is constant. You cannot add or remove values and no warning will be shown.

/Update

Update 2:

Try this example:

@State var numbers = [1,2,3,4,5,6,1]

var body: some View {
    
    VStack {
        List(numbers.indices) { index in
            let number = numbers[index]
            Text("\(number)")
        }
        Button {
            numbers.append(9)
        } label: {
            Text("add")
        }

    }
    
}

If you click on the button the 9 will be added to your array but it will not show in your List.

Adding

id: \.self

and it starts working.

like image 84
burnsi Avatar answered Sep 02 '25 14:09

burnsi