使用NavigationView/NavigationLink时SwiftUI绘图异常,点击点固定在(0,0)
问题根源
你之前通过UIApplication.shared.windows层级获取点击位置的方式存在本质缺陷:这种写法依赖UIKit的视图层级结构,当视图被NavigationView包裹或通过NavigationLink跳转后,容器会改变视图的层级关系,导致坐标系转换完全失效,最终只能拿到错误的(0,0)坐标。
解决方案
改用SwiftUI原生的手势位置获取逻辑,结合命名坐标空间,就能在任何容器环境下正确获取点击位置。具体修改如下:
修改后的完整代码
import SwiftUI struct TappableRectangle: Identifiable { let id = UUID() var points: [CGPoint] } struct IdentifiablePoint: Identifiable { let id = UUID() let point: CGPoint } struct ContentView: View { @State private var rectangles: [TappableRectangle] = [] @State private var currentPoints: [IdentifiablePoint] = [] var body: some View { NavigationView { ZStack { Color.black.opacity(0.75) .edgesIgnoringSafeArea(.all) .gesture( // 用最小距离为0的DragGesture响应点击,同时获取位置 DragGesture(minimumDistance: 0) .onEnded { gesture in if currentPoints.count < 4 { // 获取相对于ZStack的本地点击位置 let tapLocation = gesture.location(in: .named("Canvas")) currentPoints.append(IdentifiablePoint(point: tapLocation)) } if currentPoints.count == 4 { rectangles.append(TappableRectangle(points: currentPoints.map { $0.point })) currentPoints.removeAll() } } ) ForEach(rectangles) { rectangle in Path { path in path.move(to: rectangle.points[0]) path.addLine(to: rectangle.points[1]) path.addLine(to: rectangle.points[2]) path.addLine(to: rectangle.points[3]) path.closeSubpath() } .stroke(Color.blue, lineWidth: 2) } if currentPoints.count > 1 { Path { path in path.move(to: currentPoints[0].point) for i in 1..<currentPoints.count { path.addLine(to: currentPoints[i].point) } if currentPoints.count == 4 { path.closeSubpath() } } .stroke(Color.white, lineWidth: 2) } ForEach(currentPoints) { point in Circle() .fill(Color("HeliosensOrange")) .frame(width: 10, height: 10) .position(point.point) } } .coordinateSpace(name: "Canvas") // 为ZStack绑定命名坐标空间 } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
关键修改点说明
- 手势替换:用
DragGesture(minimumDistance: 0)替代TapGesture,因为TapGesture无法直接获取点击位置,而最小距离设为0的DragGesture会响应点击操作,同时能拿到精确的位置信息 - 坐标空间绑定:为ZStack添加
.coordinateSpace(name: "Canvas"),然后通过gesture.location(in: .named("Canvas"))获取相对于ZStack的本地坐标,彻底规避UIKit层级带来的坐标系混乱 - 兼容性保障:保留了你原来的自定义颜色
Color("HeliosensOrange"),如果项目中存在该颜色资源可直接使用
内容的提问来源于stack exchange,提问作者Jeredriq Demas
相关产品推荐
相关产品推荐

