使用.id()变更时LazyVGrid未重绘的问题排查
问题原因与解决方案
问题根源
你在使用Identifiable的Item结构体时,给ForEach的返回视图添加了.id(selectedItemIndex)修饰符,这干扰了SwiftUI的视图更新机制:
- 对于基于
Identifiable元素的ForEach,SwiftUI会通过元素的id来跟踪、复用子视图。添加的全局.id(selectedItemIndex)会让SwiftUI在状态变化时,错误地复用旧的子视图实例,导致即使selectedItemIndex已更新,视图的颜色、zIndex等视觉属性也不会重新计算。 - 而在使用
Range的有效代码中,由于Range元素没有Identifiable标识,SwiftUI无法通过元素ID跟踪视图,此时.id(selectedItemIndex)会触发整个ForEach的重建,从而正确更新所有子视图的视觉属性。
解决方案
移除ForEach后面的.id(selectedItemIndex)修饰符,让每个子视图自然响应selectedItemIndex的状态变化即可:
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) } } } } }
补充说明
如果在复杂场景下确实需要强制刷新整个ForEach,可以将data改为@State变量,或通过@Binding传递状态,但在当前场景下,移除全局.id修饰符已足够解决问题。
内容的提问来源于stack exchange,提问作者mcx maja
相关产品推荐
相关产品推荐

