2016-06-15 6 views
14

Были и подобные вопросы раньше, и я упомянул Capturing touches on a subview outside the frame of its superview using hitTest:withEvent: и Delivering touch events to a view outside the bounds of its parent view. Но они, похоже, не отвечают на мою конкретную проблему.Получение событий прикосновения за пределами границ

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

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

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

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

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

override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? { 
    // Convert the point to the target view's 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:

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

ответ

3

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

override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? { 
     // Convert the point to the target view's 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 не ноль и ваша таблица, сделайте следующее:

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

Нет, проблема с предыдущей реализацией заключалась в том, что точка не была правильно преобразована в систему координат UITableView. Поэтому даже в вышеприведенном случае «CGRectContainsPoint» не определяет точку, которая должна находиться в границах «UITableView». – Rajesh

+0

Я обновляю свой ответ, надеюсь, вы поможете. –

+0

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

1

Вы должны подкласс 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 
    } 
} 
+0

Идея заключалась в том, чтобы использовать элемент управления как повторно используемый компонент в любой иерархии UIView. Следовательно, использование его в качестве корневого представления не является предпочтительным решением. – Rajesh

5

Как указано:

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

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

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

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

-(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]; 
} 

Надежда, что помогает!

+0

Имеет смысл. Я не пробовал. – Rajesh

1

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

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

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

WWDC для advanced scroll views and touch handling techniques wwdc

+0

Это было первое, что я сделал. Но, как объяснялось, проблема заключается в преобразовании точки в координатную систему таблицы. – Rajesh

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

как это, контроль SuperView вызову с hittest когда касание вне контроля границ, он не возвращает ноль.

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

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