SwiftUI切换不同尺寸网格时ForEach触发数组越界问题求助
解决SwiftUI网格切换时数组越界崩溃问题
核心问题原因
切换网格尺寸时,SwiftUI视图未及时感知尺寸变化,仍基于旧网格的索引范围访问新的更小数组;或是视图复用过程中,错误关联旧索引与新数组元素,导致越界访问。
具体解决方案
1. 在ViewModel中暴露可观察的网格尺寸
将网格行列数设为@Published属性,确保视图能实时感知尺寸变化:
class RewardsViewModel: ObservableObject { @Published var currentRewardIndex = 0 private let rewards: [Reward] var currentReward: Reward { rewards[currentRewardIndex] } @Published var gridSize: Int var grid: [[RewardCell]] { currentReward.unlockedTileMatrix } init(rewards: [Reward]) { self.rewards = rewards gridSize = rewards[0].unlockedTileMatrix.count } func nextIndex() { currentRewardIndex = (currentRewardIndex + 1) % rewards.count gridSize = currentReward.unlockedTileMatrix.count } }
2. 严格基于当前网格尺寸生成视图索引
无论使用Grid还是LazyVGrid,都要基于最新的gridSize生成索引范围,避免访问旧尺寸的无效索引:
- Grid嵌套ForEach写法:
@EnvironmentObject var rewards: RewardsViewModel Grid { ForEach(0..<rewards.gridSize, id: \.self) { row in GridRow { ForEach(0..<rewards.gridSize, id: \.self) { col in RewardGridCell(cell: rewards.grid[row][col]) } } } }
- LazyVGrid写法:
确保一维数组是当前网格的扁平结果,同时给每个单元格视图绑定自身的id,避免视图复用错误:
@EnvironmentObject var rewards: RewardsViewModel private var gridAsArray: [RewardCell] { rewards.grid.flatMap { $0 } } private var gridColumns: [GridItem] { Array(repeating: GridItem(.flexible()), count: rewards.gridSize) } var body: some View { LazyVGrid(columns: gridColumns) { ForEach(gridAsArray) { cell in RewardGridCell(cell: cell) .id(cell.id) } } }
3. 给isVisible方法添加越界保护
如果isVisible方法依赖坐标参数,先判断坐标是否在当前网格的有效范围内,避免越界:
func isVisible(row: Int, col: Int) -> Bool { guard row < rewards.grid.count, col < rewards.grid[row].count else { return false } // 原有的可见性判断逻辑 return rewards.grid[row][col].isUnlocked }
内容的提问来源于stack exchange,提问作者Mercutio
相关产品推荐
相关产品推荐

