Swiftui Foreach Based on State Int

SwiftUI ForEach based on State int

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

Swift UI ForEach should only be used for *constant* data

Your problem is here:

ForEach(0..<card.numberOfShapes.rawValue) { index in
cardShape().frame(height: geometry.size.height/6)
}

This ForEach initializer's first argument is a Range<Int>. The documentation says (emphasis added):

The instance only reads the initial value of the provided data and doesn’t need to identify views across updates. To compute views on demand over a dynamic range, use ForEach/init(_:id:content:).

Because it only reads “the initial value of the provided data”, it's only safe to use if the Range<Int> is constant. If you change the range, the effect is unpredictable. So, starting in Xcode 13, the compiler emits a warning if you pass a non-constant range.

Now, maybe your range (0..<card.numberOfShapes.rawValue) really is constant. But the compiler doesn't know that. (And based on your error, it's not a constant range.)

You can avoid the warning by switching to the initializer that takes an id: argument:

ForEach(0..<card.numberOfShapes.rawValue, id: \.self) { index in
// ^^^^^^^^^^^^^
// add this
cardShape().frame(height: geometry.size.height/6)
}

ForEach - Index out of range?

Let me give you a simple example to show you what happened:

struct ContentView: View {

@State private var lowerBound: Int = 0

var body: some View {

ForEach(lowerBound..<11) { index in
Text(String(describing: index))
}

Button("update") { lowerBound = 5 }.padding()

}

}

if you look at the upper code you would see that I am initializing a ForEach JUST with a Range like this: lowerBound..<11 which it means this 0..<11, when you do this you are telling SwiftUI, hey this is my range and it will not change! It is a constant Range! and SwiftUI says ok! if you are not going update upper or lower bound you can use ForEach without showing or given id! But if you see my code again! I am updating lowerBound of ForEach and with this action I am breaking my agreement about constant Range! So SwiftUI comes and tell us if you are going update my ForEach range in count or any thing then you have to use an id then you can update the given range! And the reason is because if we have 2 same item with same value, SwiftUI would have issue to know which one you say! with using an id we are solving the identification issue for SwiftUI! About id you can use it like this: id:\.self or like this id:\.customID if your struct conform to Hash-able protocol, or in last case you can stop using id if you confrom your struct to identifiable protocol! then ForEach would magically sink itself with that.

Now see the edited code, it will build and run because we solved the issue of identification:

struct ContentView: View {

@State private var lowerBound: Int = 0

var body: some View {

ForEach(lowerBound..<11, id:\.self) { index in
Text(String(describing: index))
}

Button("update") { lowerBound = 5 }.padding()

}

}

Cannot get the @state variable in a ForEach loop working with SwiftUI

import SwiftUI

struct ContentView: View {
@State private var mainPrice = ""
@State private var mainGrade = ""

var body: some View {

let inputFields = [BindingWrapper(binding: $mainPrice), BindingWrapper(binding: $mainGrade)]

HStack {
List{
ForEach(inputFields, id: \.uuid) { value in /// here is the error
TextField("Enter data", text: value.$binding)

}
}
}
}
}

struct BindingWrapper {
let uuid: UUID = UUID()
@Binding var binding: String
}

Update @State variable (Int Counter) through ForEach button not possible?

Note that both Arrays and CharacterSelection are Value types, not Reference types. If you don't know the difference, check this page: https://developer.apple.com/swift/blog/?id=10

For your code to work, you can rewrite it like this:

struct ContentView : View {

@State var charactersList = [
CharacterSelection(id: 0, name: "Witch", count: 0),
CharacterSelection(id: 1, name: "Seer", count: 1),
CharacterSelection(id: 2, name: "Hunter", count: 0),
CharacterSelection(id: 3, name: "Knight", count: 0)
]


var body: some View {

VStack(alignment:.leading) {
ForEach(0..<charactersList.count) { i in
HStack{
Text(self.charactersList[i].name)
Spacer()
Text("\(self.charactersList[i].count)")

Button(action: { self.charactersList[i].count += 1 }) {
Text("Button")
}

}.padding(10)
}

Button(action: {
self.charactersList.append(CharacterSelection(id: self.charactersList.count, name: "something", count: 0))
}, label: {
Text("Add CharacterSelection")
})

}
}
}

SwiftUI How to Have a ForEach loop iterate based on how many days of a selected month in a Picker is

As far as I can tell you should be able to call your getRange(year:, month:) function in your ForEach. Passing in yearIndex and monthIndex.

I wrote up this little sample code to quickly test the theory. Let me know if this is what you're looking for.

-Dan

struct ContentView: View {
@State private var year = 3
@State private var month = 2


var body: some View {
List {
ForEach(0 ..< getRange(year: year, month: month)) { i in
Text("\(i)")
}
}

}

func getRange(year: Int, month: Int) -> Int {
return Calendar.current.range(of: .day, in: .month, for: Calendar.current.date(from: DateComponents(year: year, month: month + 1))!)!.count
}
}

edit to address the question in your comment;

To update your Picker label properly try changing your code to this:

Picker("Years", selection: $year) {
ForEach(0 ..< year) { index in
Text("\(index)")
}
}

See if that gives you the desired response. It's not what you're doing is entirely wrong, I'm just not super clear on what your ultimate intent is. But play around with the code, I think you're on the right track.



Related Topics



Leave a reply



Submit