2016-06-15 4 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がドロップダウンされ、選択肢の選択が可能になります。

このすべてのコントロールが最上位UIViewの直接の子である場合、上記のAppleのQ &リンクで説明したようにUIButtonでオーバーライドhitTestで正常に動作します。ただし、コントロールが別のビュー階層にある場合、UITableViewはタッチイベントを受け取りません。

次は、使用hitTestコードです:

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

編集:

私は、答えを見て、彼らは問題を解決するかどうかを確認することができていたではないため、いくつかのために謝罪すぐに注意を必要とする他のタスク。私はそれらを確認し、助けるものを受け入れます。お待ち頂きまして、ありがとうございます。

答えて

3

私はUIResponder考えていた、のようなものは:あなたはself.spinnerTableViewがnilでないことを確認したら

とそれ:このメソッドは動作しない場合は

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にしてみてください

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に示すように、コントロール自体が存在するのと同じ階層にtableviewを追加することを提案します。役立つ

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

あなたUIButtonため

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

を実装し、UITableView境界を確認する必要があります。 hitTestは、このタップポイントがビューの境界内にあるかどうか最初に確認するため、ビューに対してhitTestを呼び出します。 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時にタッチし、それがnilを返しません:私はあなたが実装pointInsideを上書きすべきだと思うadvanced scroll views and touch handling techniques wwdc

+0

これは私がやった最初のことでした。しかし、説明したように、問題はポイントをテーブルビューの座標系に変換することにあります。 – Rajesh

3

ため

WWDC。

しかし、コントロールが別のビュー階層にある場合、UITableViewはタッチイベントを受け取っていない可能性があります。

コントロールのスーパービューhitTestが返されない場合は、コントロールhitTestメソッドは呼び出されませんが、この問題は正常で、他のビューのポイントを判断できません。 コントロールの下にあるすべてのビューpointInsideメソッドをオーバーライドしない限り、それは非現実的です。

関連する問題