2016-06-15 19 views
14

Pojawiły się podobne pytania wcześniej i odniosłem się do Capturing touches on a subview outside the frame of its superview using hitTest:withEvent: i Delivering touch events to a view outside the bounds of its parent view. Ale nie wydają się odpowiadać na mój szczególny problem.Otrzymywanie zdarzeń dotykowych poza granicami

Mam niestandardowego formantu o następującej strukturze:

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

Kontrola zwyczaj nadpisuje UIButton podklasy i dodaje UITableView jako swój podrzędny. Chodzi o to, aby cała kontrola zachowywała się jak lista rozwijana. Po naciśnięciu przycisku UIButton pojawi się menu rozwijane UITableView i umożliwi wybór.

To wszystko działa dobrze z hitTest zmienione w UIButton jak opisano w Apple Q & Link powyżej, jeżeli kontrola jest bezpośrednim dzieckiem najwyższej UIView. Jeśli jednak kontrola znajduje się w innej hierarchii widoku, UITableView nie otrzymuje zdarzeń dotyku.

Poniżej znajduje się kod hitTest używane:

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:

przepraszam, że nie będąc w stanie spojrzeć na odpowiedzi i sprawdź, czy oni rozwiązać ten problem, ze względu na niektóre inne zadania wymagające natychmiastowej uwagi. Sprawdzę je i przyjmuję pomoc, która pomaga. Dziękuję za cierpliwość.

Odpowiedz

3

Myślałam o UIResponder, coś jak:

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

Jeśli ta metoda nie działa, spróbuj convertPoint z SuperView:

Po upewnieniu się, że self.spinnerTableView nie jest zerowa, a to jest twój stół, wykonaj:

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

Nie, problem z poprzednią implementacją polegał na tym, że punkt nie został poprawnie przekonwertowany na system współrzędnych "UITableView". Więc nawet w powyższym przypadku 'CGRectContainsPoint' nie identyfikuje punktu, który ma znajdować się w granicach' UITableView'. – Rajesh

+0

Zaktualizowałem swoją odpowiedź, mam nadzieję, że pomożesz. –

+0

Dzięki, ale potrzebuję ogólnego sposobu, aby to zrobić. Niezależnie od liczby poziomów hierarchicznych, kontrola jest w. – Rajesh

1

Musisz podklasować UIView i użyć go jako widoku głównego 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

Założeniem było użycie kontrolki jako składnika wielokrotnego użytku w dowolnej hierarchii UIView. Dlatego używanie go jako widoku głównego nie jest preferowanym rozwiązaniem. – Rajesh

5

Jak stwierdził:

Jednakże, jeśli kontrola jest w innym widoku hierarchii, UITableView nie jest odbieranie zdarzeń dotykowych.

Nawet jeśli zadziałało, co zrobić, jeśli wyewidencjonowałeś tę kontrolę pod innym UIView ??

W ten sposób mamy problem z konwertowaniem punktów dla hierarchii widoków, moja sugestia jest taka, że ​​jak można zobaczyć w this control, dodaje ona widok tabeli w tej samej hierarchii, w której znajduje się sama kontrola. (Np, dodaje widok tabeli na kontrolerze, gdzie został dodany ta kontrola)

częścią z linku:

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

nadzieję, że pomoże!

+0

Ma sens. Nie próbowałem tego. – Rajesh

1

Trzeba wdrożyć

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

Dla Państwa UIButton i sprawdzić UITableView granic. Ponieważ hitTest najpierw sprawdza, czy punkt zaczepienia znajduje się w zasięgu widoku, wywołuje hitTest dla widoku. więc musisz wprowadzić pointInside i zwrócić wartość true dla swoich granic.

WWDC dla advanced scroll views and touch handling techniques wwdc

+0

To była pierwsza rzecz, którą zrobiłem. Ale jak wyjaśniono, problemem jest zamiana punktu w układ współrzędnych widoku tabeli. – Rajesh

3

myślę, że należy wdrożyć nadpisane 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) 
    } 
} 

tak, Sterowanie połączeniami superview za hittest gdy dotyk poza granice kontroli, nie powróci zerowe.

Ale nadal występują problemy, jeśli kontrola znajduje się w innej hierarchii widoku, funkcja UITableView może nie otrzymywać zdarzeń dotyku.

Myślę, że ten problem jest normalny, nie można wybrać punktu widzenia innego widoku, lub nie, który pod kontrolą, jeśli kontrolny podgląd hitTest zwróci zero, metoda sterowania hitTest nie zostanie wywołana. Jeśli nie zastąpisz całego widoku pointInside metody, która pod kontrolą, ale jest nierealistyczne.

Powiązane problemy