Swift — UIPanGestureRecognizer, выбирающий все слои, когда требуется выделить только верхний слой

#swift #xcode #uiview #uipangesturerecognizer #uiview-hierarchy

#swift #xcode #uiview #uipangesturerecognizer #пользовательский интерфейс-иерархия

Вопрос:

У меня есть функция, которая создает линию перетаскивания для соединения двух кнопок друг с другом. Это работает нормально, но если некоторые кнопки перекрывают друг друга, то будут выбраны обе, если я перетащу их туда, где они перекрываются. Я хочу подключить только верхнюю кнопку.

введите описание изображения здесь

Я думаю, что проблема заключается в выборе слоев sender.location сверху и снизу. Есть ли способ сообщить sender.location, чтобы он выбирал только вид сверху? Спасибо за любой вклад и направление

 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
    }
  }
}
  

Примечание: некоторые строки кодов взяты из пользовательских расширений. Я сохранил их, поскольку они не требуют пояснений.

Спасибо за помощь

Ответ №1:

Есть способ обойти. Похоже, вы просто хотите, чтобы ваш жест заканчивался на одной кнопке над всеми остальными, таким образом, добавляя переменную вне цикла и каждый раз, когда выбирается кнопка, сравнивая с переменной ее уровня в 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()
  

Комментарии:

1. Спасибо, Лили! Это сработало, и я немного изменил код, поскольку строка «если разрешить parent = button.SuperView, parent.subviews.firstIndex(of: button) > parent.subviews.firstIndex(of: pickedButton!)» продолжала выдавать ошибку, но в конце концов это сработало

Ответ №2:

UIView Имеет свойство, вызываемое subViews , в котором элементы с более высокими индексами находятся перед элементами с более низкими индексами. Например, подвид с индексом 1 находится перед подвидом с индексом 0.

При этом, чтобы получить кнопку, которая находится сверху, вы должны отсортировать свою buttonArray так же, как организовано subViews свойство UIView . Предполагая, что все ваши кнопки являются одинаковыми UIView (возможно, это не обязательно так, но вы можете настроить их так, чтобы они были правильно отсортированы):

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

Таким образом, сохраняя вашу buttonArray сортировку таким образом, нужная вам кнопка будет содержать кнопку let point = sender.location(in: button) с более высоким индексом в массиве.

Комментарии:

1. Спасибо Ajaferson, я пробовал это, но это не решает проблему. Я думал о создании массива местоположений, когда их больше одного, и фильтрации по ним.