Swift中可拖拽且带连接线的UIViews实现技术问询
实现可拖拽圆形UIView的动态连接线方案
我之前也做过类似的需求,核心思路是把线条的绘制和圆形视图分开管理——不要在单个圆形的draw(_:)方法里画连接线,而是用一个专门的容器视图负责所有线条的绘制和更新,这样拖拽时的逻辑会清晰很多。下面是具体的实现步骤和代码示例:
1. 完善可拖拽圆形视图
你已经实现了基础的拖拽圆形,这里补充一个位置变化的回调,方便通知外部更新线条:
class CircleView: UIView { // 位置变化时的回调,通知外部重绘线条 var onPositionChanged: (() -> Void)? override init(frame: CGRect) { super.init(frame: frame) setupView() } required init?(coder: NSCoder) { super.init(coder: coder) setupView() } private func setupView() { backgroundColor = .systemBlue layer.cornerRadius = bounds.width / 2 let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePan)) addGestureRecognizer(panGesture) } @objc private func handlePan(_ gesture: UIPanGestureRecognizer) { let translation = gesture.translation(in: superview) center = CGPoint(x: center.x + translation.x, y: center.y + translation.y) gesture.setTranslation(.zero, in: superview) // 每次拖拽移动都触发回调 onPositionChanged?() } override func layoutSubviews() { super.layoutSubviews() layer.cornerRadius = bounds.width / 2 } }
2. 创建线条容器视图
这个容器专门负责绘制所有连接线,它会放在所有圆形视图的下方,避免线条遮挡圆形:
class LineContainerView: UIView { // 存储所有需要连接的圆形对 var connections: [(CircleView, CircleView)] = [] override func draw(_ rect: CGRect) { super.draw(rect) guard let context = UIGraphicsGetCurrentContext() else { return } // 设置线条样式 context.setStrokeColor(UIColor.systemGray.cgColor) context.setLineWidth(2.0) // 遍历所有连接对,绘制线条 for (circleA, circleB) in connections { let startPoint = circleA.center let endPoint = circleB.center let path = UIBezierPath() path.move(to: startPoint) path.addLine(to: endPoint) path.stroke() } } }
3. 在ViewController中整合逻辑
把圆形和线条容器结合起来,建立连接关系,并在拖拽时触发线条重绘:
class ViewController: UIViewController { private let lineContainer = LineContainerView() override func viewDidLoad() { super.viewDidLoad() setupLineContainer() setupDraggableCircles() } private func setupLineContainer() { lineContainer.frame = view.bounds lineContainer.autoresizingMask = [.flexibleWidth, .flexibleHeight] // 添加到最底层,让圆形在上面 view.insertSubview(lineContainer, at: 0) } private func setupDraggableCircles() { // 创建第一个圆形 let circle1 = CircleView(frame: CGRect(x: 100, y: 150, width: 60, height: 60)) circle1.onPositionChanged = { [weak self] in self?.lineContainer.setNeedsDisplay() } // 创建第二个圆形 let circle2 = CircleView(frame: CGRect(x: 300, y: 150, width: 60, height: 60)) circle2.onPositionChanged = { [weak self] in self?.lineContainer.setNeedsDisplay() } view.addSubview(circle1) view.addSubview(circle2) // 建立两个圆形的连接关系 lineContainer.connections.append((circle1, circle2)) } }
优化建议(可选)
如果你的场景中有大量圆形或需要更流畅的动画,可以考虑用CAShapeLayer替代draw(_:)方法:
- 每条连接线对应一个
CAShapeLayer,添加到LineContainerView的layer上 - 拖拽圆形时,只更新对应线条layer的
path属性,而不是重绘整个容器,性能会更好
另外,你还可以扩展连接关系的存储,比如给每条线添加颜色、宽度、虚线样式等属性,让线条更灵活。
内容的提问来源于stack exchange,提问作者user1391152
相关产品推荐
相关产品推荐

