0

我有 2 组独立的数据,一个用于绘制一条线(a Path),另一个用于在线某处放置一个小视图(Circle)

你可以想象它就像一个 Slider(实际上 UX 应该是这样的)

在此处输入图像描述

我想在 Red Circle View 上添加一个拖动手势,以便用户可以在线条端点之间的任何位置滑动它。但是我实施或认为的方式根本不起作用或非常糟糕。

已知的数据集是:

  • StartLine- CGPoint

  • EndLine- CGPoint

  • PositionCircle View- CGPoint

为简单起见,我添加了一些简单的点,即Top& Bottom,但实际上,我想要实现的是,通过获取纬度/经度坐标,在地图层上建立周长,将其转换为屏幕坐标空间并为这两个点绘制一条Path(线),然后让用户能够标记周长,并根据用户的需要自由地沿着周长线拖动它。(每行一个标签,没有并发症)。

话虽如此,Slider 不是一个选择。

看起来像:
在此处输入图像描述

示例代码:

struct TestView: View {
    @State var position = CGPoint(x: 60, y: 60)

    let top = CGPoint(x: 50, y: 50)
    let bottom = CGPoint(x: 300, y: 300)

    var body: some View {
        ZStack {
            Path { path in
                path.move(to: self.top)
                path.addLine(to: self.bottom)
            }
            .stroke(Color.red, lineWidth: 5)

            Circle()
                .foregroundColor(.red)
                .frame(width: 20)
                .position(self.position)
                .gesture(
                    DragGesture(minimumDistance: 0, coordinateSpace: .global)
                        .onChanged { drag in
                            print(drag.location)
                            if
                                self.top.x <= drag.location.x,
                                self.bottom.x >= drag.location.x,
                                self.top.y <= drag.location.y,
                                self.bottom.y >= drag.location.y,
                                self.pointOnLine(point: drag.location)
                            {                               
                                self.position = drag.location
                            }
                        }
                )
        }
    }   
}

检查点是否在线的辅助方法:

func pointOnLine(point: CGPoint) -> Bool {
        let dxc = point.x - top.x
        let dyc = point.y - top.y

        let dxl = bottom.x - top.x
        let dyl = bottom.y - top.y

        let cross = dxc * dyl - dyc * dxl
        return cross == 0
    }

任何帮助表示赞赏。提前致谢。

4

2 回答 2

0

得到它的工作

更新了辅助方法以获取拖动位置最近的交点和在线点。

分配给self.position哪个保持视图坚持线。

手势:

DragGesture(minimumDistance: 0, coordinateSpace: .global)
                        .onChanged { drag in
                            self.position = self.pointFrom(drag.location,
                                                           toLineSegment: self.top, self.bottom)
                        }

在线获取点的辅助方法:

private func pointFrom(_ point: CGPoint, toLineSegment start: CGPoint, _ end: CGPoint) -> CGPoint {
        let pointAndStartXDiff = point.x - start.x
        let pointAndStartYDiff = point.y - start.y
        let startAndEndXDiff = end.x - start.x
        let startAndEndYDiff = end.y - start.y

        let dotProduct = pointAndStartXDiff * startAndEndXDiff + pointAndStartYDiff * startAndEndYDiff
        let lengthSquare = startAndEndXDiff * startAndEndXDiff + startAndEndYDiff * startAndEndYDiff
        let param = dotProduct / lengthSquare

        // intersection of normal to start, end that goes through point
        var xIntersection, yIntersection: CGFloat

        if param < 0 || (start.x == end.x && start.y == end.y) {
            xIntersection = start.x
            yIntersection = start.y
        } else if param > 1 {
            xIntersection = end.x
            yIntersection = end.y
        } else {
            xIntersection = start.x + param * startAndEndXDiff
            yIntersection = start.y + param * startAndEndYDiff
        }

        return CGPoint(x: xIntersection, y: yIntersection)
    }

休息一切都一样。

结果是这样的:

在此处输入图像描述

于 2020-04-12T23:13:09.453 回答
0

您可以尝试使用沿线的位置,即使用线的角度。

您有某种拖动视图来获取当前位置:

var drag: some Gesture {
    DragGesture().onChanged { value in self.pos = CGPoint(x: value.location.x, y: value.location.y)}
}

您知道直线的初始位置和最终位置,因此您知道角度“atan(dx,dy)”。因此,从起始位置开始,您可以获得圆的下一个位置,例如 x = d cos(angle)+x0, y=d sin(angle)+y0 ...

于 2020-04-12T11:17:03.873 回答