So i get this error when running my set game app:
Image of the game screen when more of the same cards appear (All cards in the game should be unique)
ForEach<Range<Int>, Int, ModifiedContent<_ConditionalContent<_ConditionalContent<_ConditionalContent<_ConditionalContent
<ZStack<TupleView<(_ShapeView<Squiggle, ForegroundStyle>,
_StrokedShape<Squiggle>)>>, ZStack<TupleView<(ModifiedContent<_StrokedShape<StripedRect>,
_ClipEffect<Squiggle>>, _StrokedShape<Squiggle>)>>>, _StrokedShape<Squiggle>>,
_ConditionalContent<_ConditionalContent<ZStack<TupleView<(_ShapeView<Capsule, ForegroundStyle>, _StrokedShape<Capsule>)>>,
ZStack<TupleView<(ModifiedContent<_StrokedShape<StripedRect>, _ClipEffect<Capsule>>,
_StrokedShape<Capsule>)>>>, _StrokedShape<Capsule>>>, _ConditionalContent<_ConditionalContent<ZStack<TupleView<(_ShapeView<Diamond,
ForegroundStyle>, _StrokedShape<Diamond>)>>, ZStack<TupleView<(ModifiedContent<_StrokedShape<StripedRect>, _ClipEffect<Diamond>>,
_StrokedShape<Diamond>)>>>, _StrokedShape<Diamond>>>,
_FrameLayout>> count (2) != its initial count (1). `ForEach(_:content:)`
should only be used for *constant* data. Instead conform data to `Identifiable`
or use `ForEach(_:id:content:)` and provide an explicit `id`!
It doesnt seem like the card models underneath the view themselves change. Everything but the numberOfShapes on the cards stay the same. So a card that is displayed wrongly will only match if the underlying model is matched correctly and will not match like shown on view.
The error only seem to appear when i either get a set correct and press "show three more cards" or when i display more cards than is visible and then scroll up and down the screen.
The app doesnt stop with the error, but the numberOfShapes on each card changes dynamically (Like on the screenshot above). (This is most clear when i scroll to the top of the screen and then to the bottom and the number of shapes on some cards changes each time)
So i think the problem might be in the card view:
import SwiftUI
struct CardView: View {
var card: Model.Card
var body: some View {
GeometryReader { geometry in
ZStack{
let shape = RoundedRectangle(cornerRadius: 10)
if !card.isMatched {
shape.fill().foregroundColor(.white)
shape.strokeBorder(lineWidth: 4).foregroundColor(card.isSelected ? .red : .blue)
}
VStack {
Spacer(minLength: 0)
//TODO: Error here?
ForEach(0..<card.numberOfShapes.rawValue) { index in
cardShape().frame(height: geometry.size.height/6)
}
Spacer(minLength: 0)
}.padding()
.foregroundColor(setColor())
.aspectRatio(CGFloat(6.0/8.0), contentMode: .fit)
.frame(width: geometry.size.width, height: geometry.size.height)
}
}
}
@ViewBuilder private func cardShape() -> some View {
switch card.shape {
case .squiglle: shapeFill(shape: Squiggle())
case .roundedRectangle: shapeFill(shape: Capsule())
case .diamond: shapeFill(shape: Diamond())
}
}
private func setColor() -> Color {
switch card.color {
case .pink: return Color.pink
case .orange: return Color.orange
case .green: return Color.green
}
}
@ViewBuilder private func shapeFill<setShape>(shape: setShape) -> some View
//TODO: se løsning, brug rawvalues for cleanness
where setShape: Shape {
switch card.fill {
case .fill: shape.fillAndBorder()
case .stripes: shape.stripe()
case .none: shape.stroke(lineWidth: 2)
}
}
}
The cardviews are displayed in a flexible gridlayout:
import SwiftUI
struct AspectVGrid<Item, ItemView>: View where ItemView: View, Item: Identifiable {
var items: [Item]
var aspectRatio: CGFloat
var content: (Item) -> ItemView
let maxColumnCount = 6
init(items: [Item], aspectRatio: CGFloat, @ViewBuilder content: @escaping (Item) -> ItemView) {
self.items = items
self.aspectRatio = aspectRatio
self.content = content
}
var body: some View {
GeometryReader { geometry in
VStack {
let width: CGFloat = widthThatFits(itemCount: items.count, in: geometry.size, itemAspectRatio: aspectRatio)
ScrollView{
LazyVGrid(columns: [adaptiveGridItem(width: width)], spacing: 0) {
ForEach(items) { item in
content(item).aspectRatio(aspectRatio, contentMode: .fit)
}
Spacer(minLength: 0)
}
}
}
}
}
private func adaptiveGridItem(width: CGFloat) -> GridItem {
var gridItem = GridItem(.adaptive(minimum: width))
gridItem.spacing = 0
return gridItem
}
View:
import SwiftUI
struct SetGameView: View {
@StateObject var game: ViewModel
var body: some View {
VStack{
HStack(alignment: .bottom){
Text("Set Game")
.font(.largeTitle)
}
AspectVGrid(items: game.cards, aspectRatio: 2/3) { card in
CardView(card: card)
.padding(4)
.onTapGesture {
game.choose(card)
}
}.foregroundColor(.blue)
.padding(.horizontal)
Button("show 3 more cards"){
game.showThreeMoreCardsFromDeck()
}
}
}
}
Any help solving this is very much appreciated. I have spent so much time on this already.