Получение событий касания за пределами границ

Ранее были аналогичные вопросы, и я упомянул Захват касаний на подвью вне рамки его супервизора, используя hitTest: withEvent: и Передача событий касания к виду за пределами его родительского вида. Но они, кажется, не отвечают на мою конкретную проблему.

У меня есть настраиваемый элемент управления со следующей структурой:

+-------------------------------+
|           UIButton            |
+-------------------------------+
|                          |
|                          |
|        UITableView       |
|                          |
|                          |
+------------------------- +

Пользовательский элемент управления переопределяет подкласс UIButton и добавляет UITableView в качестве своего подвид. Идея состоит в том, чтобы весь контрольный акт выглядел как выпадающий список. Когда нажата кнопка UIButton, UITableView выйдет и включит выбор.

Все это отлично работает с hitTest, переопределенным в UIButton, как описано в ссылке Apple Q & A выше, если элемент управления является непосредственным дочерним элементом самого верхнего UIView. Однако, если элемент управления находится в другой иерархии представлений, UITableView не получает события касания.

Ниже приведен код использования hitTest:

override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
    // Convert the point to the target view coordinate system.
    // The target view isn't necessarily the immediate subview
    let pointForTargetView = self.spinnerTableView.convertPoint(point, fromView:self) 

    if (CGRectContainsPoint(self.spinnerTableView.bounds, pointForTargetView)) {
        // The target view may have its view hierarchy,
        // so call its hitTest method to return the right hit-test view
        return self.spinnerTableView.hitTest(pointForTargetView, withEvent:event);
    }
    return super.hitTest(point, withEvent: event)
}

Edit:

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

Ответы

Ответ 1

Как вы заявили:

Однако, если элемент управления находится в другой иерархии представлений, UITableView не получает события касания.

Даже если вы сделали это работающим, если бы вы рассматривали этот элемент под другим UIView??

Таким образом, у нас есть бремя преобразования точек для иерархии представлений, мое предложение состоит в том, что, как вы можете видеть в этот элемент управления, он добавляет табличное представление в ту же иерархию, где присутствует сам элемент управления. (например, он добавляет представление таблицы на контроллере, где был добавлен этот элемент управления)

Часть из ссылки:

-(void)textFieldDidBeginEditing:(UITextField *)textField
{
    [self setupSuggestionList];
    [suggestionListView setHidden:NO];

    // Add list to the super view.
    if(self.dataSourceDelegate && [self.dataSourceDelegate isKindOfClass:UIViewController.class])
    {
        [((UIViewController *)self.dataSourceDelegate).view addSubview:suggestionListView];
    }

    // Setup list as per the given direction
    [self adjustListFrameForDirection:dropDownDirection];
}

Надеюсь, что это поможет!

Ответ 2

Я думал о UIResponder, что-то вроде:

override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
        // Convert the point to the target view coordinate system.
        // The target view isn't necessarily the immediate subview

        var responder: UIResponder = self
        while responder.nextResponder() != nil {
            responder = responder.nextResponder()!
            if responder is UITableView {
                // Got UITableView
                break;
            }
        }
        let pointForTargetView = responder.convertPoint(point, fromView:self)
        if (CGRectContainsPoint(responder.bounds, pointForTargetView)) {
            // The target view may have its view hierarchy,
            // so call its hitTest method to return the right hit-test view
            return self.responder.hitTest(pointForTargetView, withEvent:event);
        }
        return super.hitTest(point, withEvent: event)
}

Если этот метод не работает, попробуйте convertPoint с помощью супервизора:

Если вы уверены, что self.spinnerTableView не равно nil, и это ваша таблица, выполните следующие действия:

let pointForTargetView = self.spinnerTableView.superView.convertPoint(point, fromView:self) 

Ответ 3

Я думаю, что вы должны переопределить pointInside реализовать:

class Control: UIButton {

    var dropdown: Bool = false

    override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool {
        if dropdown {
            return CGRectContainsPoint(CGRect(x: 0, y:0, width: self.bounds.width, self.bounds.height + spinnerTableView.frame.height), point)
        }
        return super.pointInside(point, withEvent: event)
    }
}

как это, управление вызовами наблюдения hittest при касании внешних границ управления, оно не вернет нуль.

Но все еще есть проблема, если элемент управления находится в другой иерархии представлений, UITableView может не получать события касания.

Я думаю, что эта проблема в норме, вы не можете решить другую точку зренияInside или нет, которая под контролем, если контрольный супервизор hittest возвращает ноль, метод управления hittest не будет вызываться. Если вы не переопределили весь метод pointInside, который под контролем, но это нереально.

Ответ 4

Вам нужно подклассифицировать UIView и использовать его как ваш корневой режим UIViewController.

class HitTestBottomView: UIView {

  override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {

    if  let hitTestView  =    self.getHitTestButtonFrom(self){
        let pointForHitTestView = hitTestView.convertPoint(point, fromView: self)
        let pointForHitTestTableView = hitTestView.spinnerTableView.convertPoint(point, fromView: self)

        if CGRectContainsPoint(hitTestView.bounds, pointForHitTestView) ||  CGRectContainsPoint(hitTestView.spinnerTableView.bounds, pointForHitTestTableView){
            return hitTestView.hitTest(pointForHitTestView, withEvent: event)
        }
    }

    return super.hitTest(point, withEvent: event)
  }

  func getHitTestButtonFrom(view:UIView) ->HitTestButton?{
    if let testView = view as? HitTestButton {
        return testView
    }

    for  subView  in view.subviews {
        if let testView = self.getHitTestButtonFrom(subView) {
            return testView
        }
    }
    return nil
  }
}

Ответ 5

Вы должны реализовать

func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool

для вашего UIButton и отметьте границы UITableView. Поскольку hitTest сначала проверяет, находится ли эта точка перехода в пределах ваших границ, а затем вызывает hitTest для вашего представления. поэтому вы должны реализовать pointInside и вернуть true для своих границ.

WWDC для расширенные виды прокрутки и методы обработки касаний wwdc