Swift - Создание соединения между двумя метками или кнопкой с использованием синей линии перетаскивания, как в Xcode - PullRequest
0 голосов
/ 07 ноября 2018

Мне удалось создать синюю линию, как в Xcode, но как бы вы узнали, когда конечная точка линии перетаскивается и отпускается над другими метками или кнопками, чтобы можно было установить соединение?

enter image description here

код

class ViewController: UIViewController {

@IBOutlet var label: UILabel!

@IBOutlet var label2: UILabel!


private lazy var lineShape: CAShapeLayer = {
    var color = hexStringToUIColor(hex: "#5DBCD2")
    let lineShape = CAShapeLayer()
    lineShape.strokeColor = UIColor.blue.cgColor
    lineShape.fillColor = color.cgColor
    lineShape.lineWidth = 2.0

    return lineShape
}()
private var panGestureStartPoint: CGPoint = .zero
private lazy var panRecognizer: UIPanGestureRecognizer = {
    return UIPanGestureRecognizer(target: self, action: #selector(panGestureCalled(_:)))
}()

override func viewDidLoad() {
    super.viewDidLoad()

    self.label.addGestureRecognizer(panRecognizer)
}

@objc func panGestureCalled(_: UIPanGestureRecognizer) {
    let currentPanPoint = panRecognizer.location(in: self.view)
    switch panRecognizer.state {
    case .began:
        panGestureStartPoint = currentPanPoint
        self.view.layer.addSublayer(lineShape)

    case .changed:
        let linePath = UIBezierPath()
        linePath.move(to: panGestureStartPoint)
        linePath.addLine(to: currentPanPoint)

        lineShape.path = linePath.cgPath
        lineShape.path = CGPath.barbell(from: panGestureStartPoint, to: currentPanPoint, barThickness: 2.0, bellRadius: 6.0)

    case .ended:
        lineShape.path = nil
        lineShape.removeFromSuperlayer()
    default: break
    }
}

func hexStringToUIColor (hex:String) -> UIColor {
    var cString:String = hex.trimmingCharacters(in: .whitespacesAndNewlines).uppercased()

    if (cString.hasPrefix("#")) {
        cString.remove(at: cString.startIndex)
    }

    if ((cString.count) != 6) {
        return UIColor.gray
    }

    var rgbValue:UInt32 = 0
    Scanner(string: cString).scanHexInt32(&rgbValue)

    return UIColor(
        red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0,
        green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0,
        blue: CGFloat(rgbValue & 0x0000FF) / 255.0,
        alpha: CGFloat(1.0)
    )
}
}

extension CGPath {
    class func barbell(from start: CGPoint, to end: CGPoint, barThickness proposedBarThickness: CGFloat, bellRadius proposedBellRadius: CGFloat) -> CGPath {
        let barThickness = max(0, proposedBarThickness)
        let bellRadius = max(barThickness / 2, proposedBellRadius)

        let vector = CGPoint(x: end.x - start.x, y: end.y - start.y)
        let length = hypot(vector.x, vector.y)

        if length == 0 {
            return CGPath(ellipseIn: CGRect(origin: start, size: .zero).insetBy(dx: -bellRadius, dy: -bellRadius), transform: nil)
        }

        var yOffset = barThickness / 2
        var xOffset = sqrt(bellRadius * bellRadius - yOffset * yOffset)
        let halfLength = length / 2
        if xOffset > halfLength {
            xOffset = halfLength
            yOffset = sqrt(bellRadius * bellRadius - xOffset * xOffset)
        }

        let jointRadians = asin(yOffset / bellRadius)
        let path = CGMutablePath()
        path.addArc(center: .zero, radius: bellRadius, startAngle: jointRadians, endAngle: -jointRadians, clockwise: false)
        path.addArc(center: CGPoint(x: length, y: 0), radius: bellRadius, startAngle: .pi + jointRadians, endAngle: .pi - jointRadians, clockwise: false)
        path.closeSubpath()

        let unitVector = CGPoint(x: vector.x / length, y: vector.y / length)
        var transform = CGAffineTransform(a: unitVector.x, b: unitVector.y, c: -unitVector.y, d: unitVector.x, tx: start.x, ty: start.y)
        return path.copy(using: &transform)!
    }
}

Код выше повторяет анимацию GIF. Я использовал приведенные ниже вопросы, чтобы создать его, если это поможет.

Нарисуйте линию, которая может растягиваться, как редактор Xcode помощник в Swift

Как воспроизвести эту синюю линию перетаскивания Xcode

Я думаю, что ответ на вышеупомянутый вопрос, но я не могу разобраться с этим, поскольку это для MacOS, а не iOS.

Любая помощь очень ценится

UPDATE

Из действительно хороших ответов я добавил приведенный ниже код в AddedPanGesture (). Это не окончательная версия кода.

 case .changed:
        let linePath = UIBezierPath()
        linePath.move(to: panGestureStartPoint)
        linePath.addLine(to: currentPanPoint)

        lineShape.path = linePath.cgPath
        lineShape.path = CGPath.barbell(from: panGestureStartPoint, to: currentPanPoint, barThickness: 2.0, bellRadius: 6.0)

        let labels = [label2, label3]
        for labelPoint in labels {
            let point = panRecognizer.location(in: labelPoint)

            if labelPoint!.layer.contains(point){
                labelPoint!.layer.borderWidth =  10
                labelPoint!.layer.borderColor =  UIColor.green.cgColor
            } else {
                labelPoint!.layer.borderWidth =  0
                labelPoint!.layer.borderColor =  UIColor.clear.cgColor

            }
        }

    case .ended:
        let labels = [label2, label3]
        for labelPoint in labels {
            let point = panRecognizer.location(in: labelPoint)

            if labelPoint!.layer.contains(point){
                labelPoint!.layer.borderWidth =  2
                labelPoint!.layer.borderColor =  UIColor.green.cgColor
            } else {
                lineShape.path = nil
                lineShape.removeFromSuperlayer()
                labelPoint!.layer.borderWidth =  0
                labelPoint!.layer.borderColor =  UIColor.clear.cgColor

            }
        }

enter image description here

Если есть способ получше, мне бы понравился ввод. Спасибо!

Ответы [ 2 ]

0 голосов
/ 07 ноября 2018

Я просто обновляю код для вашего удобства.

 @objc func panGestureCalled(_: UIPanGestureRecognizer) {
    let currentPanPoint = panRecognizer.location(in: self.view)
    switch panRecognizer.state {
    case .began:
        panGestureStartPoint = currentPanPoint
        self.view.layer.addSublayer(lineShape)

    case .changed:
        let linePath = UIBezierPath()
        linePath.move(to: panGestureStartPoint)
        linePath.addLine(to: currentPanPoint)

        lineShape.path = linePath.cgPath
        lineShape.path = CGPath.barbell(from: panGestureStartPoint, to: currentPanPoint, barThickness: 2.0, bellRadius: 6.0)


        label2.layer.borderWidth =  hasDestine(panRecognizer) ? 10 :   0
        label2.layer.borderColor =  hasDestine(panRecognizer) ? UIColor.green.cgColor :   UIColor.clear.cgColor

    case .ended:
        if  (!hasDestine(panRecognizer)){
        lineShape.path = nil
        lineShape.removeFromSuperlayer()}
        label2.layer.borderWidth =   0
    default: break
    }
}

func hasDestine(_ panRecognizer: UIPanGestureRecognizer)-> Bool {
    let point = panRecognizer.location(in: label2)
    return   label2.layer.contains(point)
}
0 голосов
/ 07 ноября 2018

CGRect реализует метод contains(_:), который позволяет определить, содержит ли прямоугольник точку.

Вам необходимо добавить логику, которая гарантирует, что ваша начальная точка перетаскивания находится внутри кнопки, а ваша конечная точка находится внутри другой кнопки, используя рамки этих кнопок в качестве опрашиваемых вами линий. Если вы обнаружите, что пользователь переместился с одной кнопки на другую, тогда ваш обработчик .ended (как упомянул М. Абубакер Маджид в своем комментарии) должен обнаружить это и каким-то образом добавить постоянную ссылку. Как вы это делаете, зависит от вас. Вы можете использовать отдельный слой формы для хранения линий, которые вы хотите сохранить, а также массив начальных / конечных точек соединительных линий или различные другие подходы.

...