2

I have function which creates a drag line to connect 2 buttons to each other. This works fine but if some buttons overlap each other, it will select both if I drag over where they overlap. I only want to connect the top button.

enter image description here

I think the issue is with the sender.location selecting layers on top and below. Is there a way to tell the sender.location to only select the top view? Thanks for any input and direction

func addPanReconiser(view: UIView){

    let pan = UIPanGestureRecognizer(target: self, action: #selector(DesignViewController.panGestureCalled(_:)))
    view.addGestureRecognizer(pan)
}

@objc func panGestureCalled(_ sender: UIPanGestureRecognizer) {

    let currentPanPoint = sender.location(in: self.view)

    switch sender.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)

        for button in buttonArray {
            let point = sender.location(in: button)

            if button.layer.contains(point) {
                button.layer.borderWidth = 4
                button.layer.borderColor = UIColor.blue.cgColor
            } else {
                button.layer.borderWidth = 0
                button.layer.borderColor = UIColor.clear.cgColor
            }
        }

    case .ended:

        for button in buttonArray {
            let point = sender.location(in: button)

            if button.layer.contains(point){

                //DO my Action here
                lineShape.path = nil
                lineShape.removeFromSuperlayer()

            }
        }
    default: break
    }
  }
}

Note: some of the lines of codes are from custom extensions. I kept them in as they were self explanatory.

Thanks for the help

STerrier
  • 3,755
  • 1
  • 16
  • 41

2 Answers2

1

There is a way to walk around. It seems like you simply want your gesture end up at one button above all the others, thus by adding a var outside the loop and each time a button picked, comparing with the var of its level at z.

case .ended:
        var pickedButton: UIButton?
        for button in buttonArray {
            let point = sender.location(in: button)

            if button.layer.contains(point){
                if pickedButton == nil {
                    pickedButton = button
                } else {
                    if let parent = button.superView, parent.subviews.firstIndex(of: button) > parent.subviews.firstIndex(of: pickedButton!) {
                        pickedButton = button
                    }
                }
            }
        }
        //DO my Action with pickedButton here
        lineShape.path = nil
        lineShape.removeFromSuperlayer()
LiLi Kazine
  • 195
  • 2
  • 9
  • Thanks Lili! This worked and I did change the code slightly as the line "if let parent = button.superView, parent.subviews.firstIndex(of: button) > parent.subviews.firstIndex(of: pickedButton!)" kept throwing an error but in the end it worked out – STerrier Mar 07 '19 at 00:51
0

A UIView has a property called subViews where elements with higher indexes are in front of the ones with lower indexes. For instance, subView at index 1 is in front of subView with index 0.

That being said, to get the button that's on top, you should sort your buttonArray the same way subViews property of UIView is organized. Assuming that your buttons are all siblings of the same UIView (this might not be necessarily the case, but you can tweak them so you get them sorted correctly):

var buttonArray = view.subviews.compactMap { $0 as? UIButton }

Thus, keeping your buttonArray sorted that way, the button you want is the one that contains let point = sender.location(in: button) with higher index in the array.

alanpaivaa
  • 1,959
  • 1
  • 14
  • 23
  • Thanks Ajaferson, I tried this but it's not solving the issue. I was thinking of creating an array of the location when there is more than one and filter through. – STerrier Mar 06 '19 at 04:23