在Swift中可拖动连接的UIViews

时间:2018-02-21 20:56:18

标签: ios swift

我正在尝试创建一些可通过线连接的可拖动UIView。见下图:

enter image description here

我可以通过创建一个类来创建可拖动的圆圈,该类是UIView的子类并覆盖绘制函数

override func draw(_ rect: CGRect) {
    let path = UIBezierPath(ovalIn: rect)
    let circleColor:UIColor

    switch group {
    case .forehead:
        circleColor = UIColor.red
    case .crowsFeetRightEye:
        circleColor = UIColor.green
    case .crowsFeetLeftEye:
        circleColor = UIColor.blue
    }

    circleColor.setFill()
    path.fill()
}

然后为拖动添加平移手势识别器

func initGestureRecognizers() {
    let panGR = UIPanGestureRecognizer(target: self, action: #selector(DragPoint.didPan(panGR:)))
    addGestureRecognizer(panGR)
}

@objc func didPan(panGR: UIPanGestureRecognizer) {

    if panGR.state == .changed {
        self.superview!.bringSubview(toFront: self)
        let translation = panGR.translation(in: self)

        self.center.x += translation.x
        self.center.y += translation.y

        panGR.setTranslation(CGPoint.zero, in: self)

    }

}

但是,我完全不知道如何在连接线上进行操作,并在拖动时将起点/终点点到相应的圆圈。有没有人可以帮助或指出我正确的方向?

2 个答案:

答案 0 :(得分:6)

您希望CAShapeLayers使用UIBezier路径绘制圆圈之间的线条,然后在用户移动视图时更改路径。

这是一个展示实施的游乐场。您可以将其剪切并粘贴到游乐场中以查看它的实际效果。

//: A UIKit based Playground for presenting user interface

import UIKit
import PlaygroundSupport

class CircleView : UIView {

    var outGoingLine : CAShapeLayer?
    var inComingLine : CAShapeLayer?
    var inComingCircle : CircleView?
    var outGoingCircle : CircleView?

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.layer.cornerRadius = self.frame.size.width / 2
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func lineTo(circle: CircleView) -> CAShapeLayer {
        let path = UIBezierPath()
        path.move(to: self.center)
        path.addLine(to: circle.center)

        let line = CAShapeLayer()
        line.path = path.cgPath
        line.lineWidth = 5
        line.strokeColor = UIColor.red.cgColor
        circle.inComingLine = line
        outGoingLine = line
        outGoingCircle = circle
        circle.inComingCircle = self
        return line
    }
}

class MyViewController : UIViewController {

    let circle1 = CircleView(frame: CGRect(x: 100, y: 100, width: 50, height: 50))
    let circle2 = CircleView(frame: CGRect(x: 100, y: 200, width: 50, height: 50))
    let circle3 = CircleView(frame: CGRect(x: 100, y: 300, width: 50, height: 50))
    let circle4 = CircleView(frame: CGRect(x: 100, y: 400, width: 50, height: 50))

    override func loadView() {
        let view = UIView()
        view.backgroundColor = .white
        self.view = view

        circle1.backgroundColor = .red
        view.addSubview(circle1)

        circle2.backgroundColor = .red
        view.addSubview(circle2)

        circle3.backgroundColor = .red
        view.addSubview(circle3)

        circle4.backgroundColor = .red
        view.addSubview(circle4)

        circle1.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(didPan(gesture:))))

        circle2.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(didPan(gesture:))))

        circle3.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(didPan(gesture:))))

        circle4.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(didPan(gesture:))))

        view.layer.addSublayer(circle1.lineTo(circle: circle2))
        view.layer.addSublayer(circle2.lineTo(circle: circle3))
        view.layer.addSublayer(circle3.lineTo(circle: circle4))
    }

    @objc func didPan(gesture: UIPanGestureRecognizer) {
        guard let circle = gesture.view as? CircleView else {
            return
        }
        if (gesture.state == .began) {
            circle.center = gesture.location(in: self.view)
        }
        let newCenter: CGPoint = gesture.location(in: self.view)
        let dX = newCenter.x - circle.center.x
        let dY = newCenter.y - circle.center.y
        circle.center = CGPoint(x: circle.center.x + dX, y: circle.center.y + dY)


        if let outGoingCircle = circle.outGoingCircle, let line = circle.outGoingLine, let path = circle.outGoingLine?.path {

            let newPath = UIBezierPath(cgPath: path)
            newPath.removeAllPoints()
            newPath.move(to: circle.center)
            newPath.addLine(to: outGoingCircle.center)
            line.path = newPath.cgPath
        }

        if let inComingCircle = circle.inComingCircle, let line = circle.inComingLine, let path = circle.inComingLine?.path {

            let newPath = UIBezierPath(cgPath: path)
            newPath.removeAllPoints()
            newPath.move(to: inComingCircle.center)
            newPath.addLine(to: circle.center)
            line.path = newPath.cgPath
        }
    }
}

// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
PlaygroundPage.current.needsIndefiniteExecution = true

gif showing the code working

答案 1 :(得分:0)

UIBezierPaths和CAShapeLayers可以解决此问题,但是有一种更简便的方法。

  1. 在视图后面添加UIImageView
  2. 每次视图移动时调用该函数并使用设置图像 UIGraphicsImageRenderer。

    func drawLines(viewOne: UIView, viewTwo: UIView) {
    
    let rendered = UIGraphicsImageRenderer(bounds: view.bounds)
        imageView.image = rendered.image(actions: { (ctx) in
    
                ctx.cgContext.setLineWidth(2)
                ctx.cgContext.strokeLineSegments(between: [viewOne.center, viewTwo.center])
            }
        })
        }