You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用NavigationView/NavigationLink时SwiftUI绘图异常,点击点固定在(0,0)

问题解决:NavigationView下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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 12:02:15