SwiftUI拖拽手势问题:网格遮罩未从手指落点开始移除
解决SwiftUI拖拽移除遮罩时从左上角开始的问题
你的问题出在每个遮罩单元格单独绑定了DragGesture,此时手势回调里的value.location是相对于当前单元格的局部坐标,不是整个网格的全局坐标。比如你拖拽到任意单元格,它的局部x、y都会是0到50之间,除以50后得到0,所以总是修改左上角的topGridRevealed[0][0]。
修复方案
把DragGesture绑定到整个遮罩网格的外层容器上,这样获取的坐标是相对于整个网格的全局坐标,计算出来的行列位置才准确。同时可以优化逻辑,避免重复设置同一个单元格为true。
修改后的完整代码:
import SwiftUI struct ContentView: View { @State var bottomGridColors: [[Color]] = Array(repeating: [.red, .orange, .yellow, .green, .blue, .purple, .pink, .gray], count: 8) @State private var topGridRevealed = [[Bool]](repeating: Array(repeating: false, count: 8), count: 8) // 记录已经处理过的单元格,避免重复操作 @State private var processedCells = Set<CGPoint>() var body: some View { ZStack { // 底层颜色网格 VStack(spacing: 0) { ForEach(0..<8, id: \.self) { row in HStack(spacing: 0) { ForEach(0..<8, id: \.self) { col in Rectangle() .fill(bottomGridColors[row][col]) .frame(width: 50, height: 50) } } } } // 顶层遮罩网格,把手势绑定到外层VStack VStack(spacing: 0) { ForEach(0..<8, id: \.self) { row in HStack(spacing: 0) { ForEach(0..<8, id: \.self) { col in Rectangle() .fill(topGridRevealed[row][col] ? .clear : .black) .frame(width: 50, height: 50) } } } } .gesture( DragGesture(minimumDistance: 0) .onChanged { value in let cellSize: CGFloat = 50 // 计算当前触摸点对应的行列 let x = Int(value.location.x / cellSize) let y = Int(value.location.y / cellSize) // 检查行列是否在有效范围内,且未被处理过 guard x >= 0, x < 8, y >= 0, y < 8, !processedCells.contains(CGPoint(x: x, y: y)) else { return } topGridRevealed[y][x] = true processedCells.insert(CGPoint(x: x, y: y)) } .onEnded { _ in // 拖拽结束后清空已处理记录,下次拖拽可以重新操作 processedCells.removeAll() } ) } } } struct ContentViewPreview: PreviewProvider { static var previews: some View { ContentView() } }
关键修改说明
- 手势移到外层:将DragGesture绑定到整个遮罩网格的VStack上,确保
value.location是相对于整个网格的坐标,计算出来的x、y是正确的行列索引。 - 添加已处理单元格记录:用
Set<CGPoint>记录已经移除遮罩的单元格,避免拖拽过程中重复设置同一个单元格,提升性能。 - 优化初始化代码:用
Array(repeating: ...)简化颜色网格的初始化,代码更简洁。
如果需要适配不同屏幕尺寸,建议用GeometryReader获取网格的实际宽高,动态计算单元格大小,而不是固定50。比如:
GeometryReader { geo in let cellSize = min(geo.size.width, geo.size.height) / 8 // 后续用cellSize代替固定的50 }
内容的提问来源于stack exchange,提问作者maaronhoffman
相关产品推荐
相关产品推荐

