SwiftUI onDrag捕获原始值问题
SwiftUI onDrag捕获原始值问题

SwiftUI onDrag捕获原始值问题

问题描述

在开发《方方块》游戏的过程中,发现在第一次拖动时,并不会显示方块阴影。但是第二次拖动时,会显示方块阴影。

经过代码排查,发现在第一次放置时,发现有一个棋盘的原点是 (0.0, 0.0)。

gridOrigin:(0.0, 0.0)

通过排查发现,棋盘的原点实际是可以正常获取的:

GameGridView(grid: grid, shadowPosition: shadowPosition, shadowBlock: shadowBlock)
    .background {
        GeometryReader { gridGeo in
            Color.clear
                .onAppear {
                    DispatchQueue.main.async {
                        let gridPosition = gridGeo.frame(in: .global).origin
                    gridOrigin = gridPosition
                    print("使用延迟获取的网格原点 gridOrigin: \(gridOrigin)")
                }
            }
        }
}

Xcode输出的代码为:

使用延迟获取的网格原点 gridOrigin: (7.5, 212.0)

但是,在拖动方块的过程中,计算阴影的方法获取的gridOrigin是(0, 0)。

进入shadowBlock方法
start:(64.66666158040366, 64.66665649414062)
end:(22.0, -9.0)
origins:(127.5, 602.0)
gridOrigin:(0.0, 0.0)

经过一系列排查的工作,发现问题在于onDrag方法中的gridOrigin捕获的是原始值。

DraggableBlockView(block: block, 
    GestureOffset: GestureOffset,
     onDrag :{ start, end, geo  in
        shadowBlock(block, start, end, geo, item)
    },
    onDrop: {start, end, geo  in
      placeBlock(block, start, end, geo, item)
})

在DraggableBlockView视图中,shadowBlock和placeBlock的参数是一致的,这两个方法都是从外部获取的gridOrigin:

// 显示放置方块的阴影
func shadowBlock(_ block: Block, _ start: CGPoint, _ end: CGSize, _ origins: CGPoint, _ indices : Int) {
    let CalculateY = touchOffsetY - gridOrigin.y
    // ...
}

// 放置方块
func placeBlock(_ block: Block, _ start: CGPoint, _ end: CGSize, _ origins: CGPoint, _ indices : Int) {
    let CalculateY = touchOffsetY - gridOrigin.y 
    // ...
}

但是只有在调用onDrop方法时,才能获取到真实的gridOrigin值。

经过查询了解到,在SwiftUI的onDrag中,gridOrigin可能被捕获为原始值(0,0),而 SwiftUI 的状态更新是异步的,所以当 onDrag 触发时,它可能还没有拿到最新的 gridOrigin。

.onDrag { _ in
    print("移动中,gridOrigin: \(gridOrigin)") // 可能仍然是 (0,0)
}

这里 onDrag 捕获的是当时的 gridOrigin 值,而不是一个实时更新的引用。

解决方案

SwiftUI 异步更新 UI,onDrag 可能发生在 gridOrigin 更新前。

DispatchQueue.main.async 推迟执行代码,确保 gridOrigin 已经正确更新。

DraggableBlockView(block: block, 
    GestureOffset: GestureOffset,
     onDrag :{ start, end, geo  in
      DispatchQueue.main.async {
          shadowBlock(block, start, end, geo, item)
      }
  },
    onDrop: {start, end, geo  in
      placeBlock(block, start, end, geo, item)
  })

使用DispatchQueue.main.async 把代码推到下一次主线程循环中执行,这就保证了 gridOrigin 已经更新,获取到的是最新值。

总结

onDrag 捕获的是旧的 gridOrigin,所以直接 print(gridOrigin) 可能仍然是 (0,0)。

DispatchQueue.main.async 让代码在 UI 更新后执行,因此 gridOrigin 是最新的。

shadowBlock 可能在 UI 更新后才调用,所以能获取到最新的 gridOrigin。

最后,通过DispatchQueue.main.async实现onDrag捕获最新值。

   

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开发者。

欢迎加入我们的 微信交流群QQ交流群,交流更多精彩内容!
微信交流群二维码 QQ交流群二维码

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注