SwiftUI中仅更新选中GridItem状态值的问题
解决LazyVGrid中单个GridItem独立切换状态的问题
你当前的问题核心是所有GridItem共享了同一个isRed状态变量,所以切换时会全局同步。要实现单个条目独立控制,需要给每个GridItem单独维护状态,具体修改如下:
步骤1:改用字典存储每个条目的状态
把原来的单个@State var isRed换成字典,用条目ID作为键,对应每个条目的isRed状态:
@State private var itemStates: [Int: Bool] = [:]
字典的好处是可以按需存储每个条目的状态,不用提前初始化1092个值,后续通过default: false会自动处理未初始化的条目。
步骤2:修改GridItemView的绑定逻辑
在ForEach中,给每个GridItemView绑定对应ID的字典值,而非全局的isRed:
GridItemView(id: gridItemIndex, isRed: $itemStates[gridItemIndex, default: false])
这里的default: false表示如果字典里还没有该ID的记录,就默认使用false,同时会自动把这个键值对添加到字典中。
步骤3:修复ContextMenu的操作逻辑
直接切换对应ID的状态即可,无需通过视图对象访问属性(视图对象的属性不应直接修改,要通过绑定):
Button(action: { $itemStates[gridItemIndex, default: false].wrappedValue.toggle() }) { Label("Toggle isRed for gridItemIndex \(gridItemIndex)", systemImage: "paintbrush.fill") }
修改后的完整代码
GridItemView(无需修改,保持原样)
import SwiftUI struct GridItemView: View { var id: Int @Binding var isRed: Bool var body: some View { VStack { Text("Hello") Image(systemName: "globe.europe.africa") } .foregroundStyle(isRed ? .red : .black) .padding() .border(.black) } }
修改后的ContentView
import SwiftUI struct ContentView: View { let gridLayout = Array(repeating: GridItem(.flexible(), spacing: 0), count: 4) let numberOfGridItems = 1092 // 用字典存储每个条目的独立状态 @State private var itemStates: [Int: Bool] = [:] var body: some View { VStack { ScrollView(.vertical) { LazyVGrid(columns: gridLayout) { ForEach(0..<numberOfGridItems, id: \.self) { gridItemIndex in GridItemView(id: gridItemIndex, isRed: $itemStates[gridItemIndex, default: false]) .contextMenu { Button(action: { // 直接切换对应ID的状态 $itemStates[gridItemIndex, default: false].wrappedValue.toggle() }) { Label("Toggle isRed for gridItemIndex \(gridItemIndex)", systemImage: "paintbrush.fill") } } } } .scrollTargetLayout() } .scrollTargetBehavior(.viewAligned) } } }
原理说明
通过字典itemStates,每个GridItem的ID对应唯一的isRed状态,切换时只会修改当前ID对应的键值对,不会影响其他条目。SwiftUI的绑定机制会自动监听字典的变化,只刷新状态改变的那个GridItem,同时LazyVGrid的视图复用特性也能保证性能不受影响。
内容的提问来源于stack exchange,提问作者RyanTCB
相关产品推荐
相关产品推荐

