I’m trying to update LazyVGrid item properties when tapped and it worked on plain Ints but stopped working when numbers are wrapped in collection.
code that doesn’t work:
import SwiftUI
struct Item : Identifiable, Hashable {
var id: Int
}
let data = (1...12).map { index in
Item(id: index)
}
struct ZIndexInLazyVGridUpdateContainerExperiment: View {
@State private var selectedItemIndex: Int = 5
var body: some View {
ZStack {
Color.black.ignoresSafeArea()
ScrollView {
LazyVGrid(columns: [GridItem(), GridItem(), GridItem()]) {
ForEach(data) { item in
Rectangle()
.fill(Color(
red: 0,
green: 0.5,
blue: selectedItemIndex == item.id ? 1 : Double(item.id)/24.0 + 0.5))
.zIndex(selectedItemIndex == item.id ? 1 : 0)
.frame(width: 180, height: 180)
.border(.black)
.rotationEffect(.degrees(45))
.onTapGesture {
selectedItemIndex = item.id
print("selected item (selectedItemIndex)")
}
}
.id(selectedItemIndex)
}
}
}
}
}
code that works, that was slightly changed:
import SwiftUI
struct ZIndexInLazyVGridUpdateExperiment: View {
@State private var selectedItemIndex: Int = 5
var body: some View {
ZStack {
Color.black.ignoresSafeArea()
ScrollView {
LazyVGrid(columns: [GridItem(), GridItem(), GridItem()]) {
ForEach(1..<13) { index in
Rectangle()
.fill(Color(
red: selectedItemIndex == index ? 1 : Double(index)/24.0 + 0.5,
green: 0,
blue: 0))
.zIndex(selectedItemIndex == index ? 1 : 0)
.frame(width: 180, height: 180)
.border(.black)
.rotationEffect(.degrees(45))
.onTapGesture {
selectedItemIndex = index
print("selected item (selectedItemIndex)")
}
}
.id(selectedItemIndex)
}
}
}
}
}
Previously (in working code version), item, when tapped, would print to console, update rectangle color and update zIndex. After wrapping Int in Item struct, tapping still is logged, but nothing in LazyVGrid changes visually. I can’t figure out what went wrong.
2
Answers
try this approach attaching
.id(selectedItemIndex)
to theLazyVGrid
EDIT-1:
You could also try a similar approach with this example code:
As Workingdog showed, you need to have the selected
id
on theLazyVGrid
, not on theForEach
. I suspect that the lazy optimisation part of theLazyVGrid
works out that none of the bound IDs are changing in the ForEach, so it doesn’t redraw the view.If you remove the
LazyVGrid
so that you only have the rectangles in the scroll view, changing theid
of theForEach
is sufficient.In addition to moving the
id
for the selected item to theLazyVGrid
I would avoid using the.id
property; this doesn’t change the behaviour of your code, but it is cleaner.Identifiable
means you can compare directly