SwiftUI中如何让线条跟随CGPoint拖拽移动?
SwiftUI拖拽节点时线条同步跟随移动的实现方案
我在SwiftUI中创建了两个可拖拽的节点(CGPoint),并绘制了连接它们的线条。需求是拖拽第二个节点时,线条能同步跟随其移动,保持连接状态,但目前线条并未随拖拽动作移动,请问该如何修复?
原代码
Point.swift
import SwiftUI struct Point : View { var position: CGPoint init(_ position: CGPoint) { self.position = position } var body: some View { Circle() .frame(width: 20) .foregroundColor(.white) .position(x: position.x, y: position.y) } }
LineWithPoints.swift
struct LineWithPoints : View { @State var point1 = Point(.init(x: 0, y: 0)) @State var point2 = Point(.init(x: 450, y: 0)) @State private var offset1 = CGSize.zero var body: some View { let lineBetween = LineBetween(pointPosition: $point2.position) ZStack { lineBetween.stroke(.red, lineWidth: 2) point1; point2 .offset(x: offset1.width, y: offset1.height) .gesture( DragGesture() .onChanged { value in let x = value.startLocation.x let y = value.startLocation.y let w = value.translation.width let h = value.translation.height offset1 = .init(width: (x + w) - point2.position.x, height: (y + h) - point2.position.y) } ) } } struct LineBetween : Shape, @unchecked Sendable { @Binding var pointPosition: CGPoint func path(in rect: CGRect) -> Path { var path = Path() path.move(to: CGPoint.zero) path.addLine(to: pointPosition) path.closeSubpath() return path } } }
ContentView.swift
struct ContentView : View { var body: some View { ZStack { Color.black.ignoresSafeArea() LineWithPoints() } } }
问题原因
原代码的核心问题:
- 使用
offset移动point2时,并未更新point2自身的position属性,导致绑定给LineBetween的pointPosition值无变化,线条无法更新。 LineBetween的路径起点固定为CGPoint.zero,但point1的实际位置可能并非原点,导致线条起点错误。
修复方案
1. 修改Point视图,支持双向绑定
将Point的position改为@Binding,让父视图能直接修改它的位置:
import SwiftUI struct Point : View { @Binding var position: CGPoint init(_ position: Binding<CGPoint>) { self._position = position } var body: some View { Circle() .frame(width: 20) .foregroundColor(.white) .position(x: position.x, y: position.y) } }
2. 重构LineWithPoints视图,直接更新节点位置
移除offset1变量,拖拽时直接修改point2的坐标,同时让LineBetween连接两个节点的实际位置:
struct LineWithPoints : View { @State private var point1Pos = CGPoint(x: 0, y: 0) @State private var point2Pos = CGPoint(x: 450, y: 0) var body: some View { ZStack { LineBetween(from: point1Pos, to: point2Pos) .stroke(.red, lineWidth: 2) Point($point1Pos) Point($point2Pos) .gesture( DragGesture() .onChanged { value in // 直接将point2的位置更新为拖拽实时位置 point2Pos = value.location } ) } } struct LineBetween : Shape, @unchecked Sendable { let from: CGPoint let to: CGPoint func path(in rect: CGRect) -> Path { var path = Path() path.move(to: from) path.addLine(to: to) return path } } }
3. ContentView保持不变
struct ContentView : View { var body: some View { ZStack { Color.black.ignoresSafeArea() LineWithPoints() } } }
说明
- 拖拽时直接修改
point2Pos的坐标,SwiftUI会自动触发视图更新,LineBetween会使用最新的两点坐标重新绘制线条。 - 将
Point的position改为@Binding,确保父视图的状态变化能同步到子视图。 LineBetween现在直接接收两个节点的坐标,绘制逻辑更清晰,避免了原点依赖问题。
内容的提问来源于stack exchange,提问作者Kurt
相关产品推荐
相关产品推荐

