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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 09:24:16