web-dev-qa-db-ja.com

UIGestureRecognizerからサブビューを除外する

いくつかの「サブビュー」を含むUIView(「コンテナービュー」)があります。 UITapGestureRecognizerをコンテナービューに追加し、コンテナービューの内側でサブビューの外側の領域に触れるとアクティブになるようにします。

現時点では、サブビュー内を含むコンテナービュー内の任意の場所をタッチすると、ジェスチャー認識機能がアクティブになります。

実装は次のようになります。コントローラで:

ContainerView *containerView = [[ContainerView alloc] initWithSubViews:array];
UITapGestureRecognizer *tap = [UITapGestureRecognizer alloc] initWithTarget:self action:@selector(someSelector)];
[containerView addGestureRecognizer:tap];
[self.view addSubView:containerView];

ContainerView.m

-(id)initWithSubviews:(NSArray *)array {
    for (subView *s in array) {
        [self addSubView:s];
    }
    return self;
}

問題は、サブビューの後にジェスチャー認識機能が追加されるために発生すると思います。それが真の場合、ソリューションはinitWithSubViewsメソッドを2つの別々のメソッドに分割する必要がありますが、これは避けたいと思います。

ありがとうございました

32
Awais Hussain

iOS 6では、この正確な問題を解決する優れた新機能が導入されています。UIView(サブビュー)はgestureRecognizerShouldBegin:(スーパービューに接続されたジェスチャー認識機能)からNOを返すことができます。実際、これは、一部のジェスチャ認識機能に関するUIViewサブクラスのデフォルトです(たとえば、スーパービューにアタッチされたUITapGestureRecognizerに関するUIButton)。

このトピックに関する私の本を参照してください: http://www.apeth.com/iOSBook/ch18.html#_gesture_recognizers

12
matt

以下の簡単な方法を使用しました。 完全に機能します!

UIGestureRecognizerDelegate関数を実装し、スーパービューでのタッチのみを受け入れ、サブビューでのタッチを受け入れない:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    if (touch.view != _mySuperView) { // accept only touchs on superview, not accept touchs on subviews
        return NO;
    }

    return YES;
}
39
vietstone

次のようにして、なんとか動作させることができました。

UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGestureHandler:)];

// ...

-(void) tapGestureHandler:(UITapGestureRecognizer *)sender {
    CGPoint point = [sender locationInView:sender.view];
    UIView *viewTouched = [sender.view hitTest:point withEvent:nil];
    if ([viewTouched isKindOfClass:[ThingIDontWantTouched class]]) {
        // Do nothing;
    } else {
        // respond to touch action
    }
}
13
Awais Hussain

デリゲートメソッドを追加および設定することを忘れないでください-

UIViewControllerの.hファイルにこのデリゲートを含めます

@interface YourViewController: UIViewController<UIGestureRecogniserDelegate>

次に、tapGestureオブジェクトを作成する場所(たとえば、viewDidLoad内)

    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(actionMethodYouWantToHappenOnTap)];  

    tap.delegate = self;  //Remember to set delegate! Otherwise the delegate method won't get called.

    [self.view addGestureRecognizer:tap];

デリゲートメソッドを設定することを忘れないtap.delegate = self;すると、タップのデリゲートメソッドがタップで発生します。

このメソッド内では、タップジェスチャー認識を使用したい場合に処理します。私のコードでは、特定のサブビューが表示されている場合にタップを無視するようにしました。

-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{

     if (!mySubview.hidden){
     return NO;   //This fired if said subview was visible, though whatever the condition of where and when you want tap to work, can be handled within this delegate method.
     }

    return YES;
}

これが同じ問題を抱えている他の人に役立つことを願っています。私が見つけたデリゲートメソッドを設定することを思い出すことは、簡単に見落とされるものでした。

乾杯

4
Jim Tierney

答えがたくさんあり、別の代替ソリューションを追加しています。タッチを受け取りたくないサブビューのビュータグを1として初期化し、以下を実行します。

- (void)viewDidLoad {
    [super viewDidLoad];
    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
    tap.delegate = self;
    [self.view addGestureRecognizer:tap];
}

-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    if (touch.view.tag == 1) {
        return NO;
    }
    return YES;
}

-(void)handleTap:(id)sender
{
//Handle tap...
}

このように、タッチジェスチャは、必要なビューからのみ受信されます。

3

ContainerViewにUITapGestureRecognizerを追加すると、ContainerView全体で応答するはずです。サブビューは気にしません。

ジェスチャーの場所を確認し、サブビューの位置にある場合は、ジェスチャーアクションをスキップしてください。

- (void) tapGestureHandler:(UIGestureRecognizer *) gestureRecognizer {


    CGPoint tapPoint = [gestureRecognizer locationInView:nil]; 

   //check your tapPoint contains ur subview frame, skip.else do ur actions
}
0
Shamsudheen TK

追加:

私はもっ​​と良いものを考えました。

あなたのハンドラで

-(void)tapGestureHandler:(UITapGestureHandler)gesture

gesture.Viewがスーパービューかどうかを確認します。サブビューがタップされると、サブビューを返します。

================================================== ==

スーパービュー内でオーバーライドすることをお勧めします。

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event

これは、ジェスチャーがビュー内にあるかどうかを判断するために呼び出されます

この質問に対する回答を参照して、デフォルトでどのように動作するかを確認してください。

iOSのイベント処理-hitTest:withEvent:とpointInside:withEvent:はどのように関連していますか?

ポイントがサブビューのいずれかにあるかどうかを確認できます。はいの場合はnilを返し、そうでない場合はselfを返します。

OR

各サブビューに、何もしないタプスチャー認識機能を追加します。サブビューのジェスチャー認識機能は、デフォルトでスーパービューのジェスチャー認識機能をキャンセルします。サブビュー数が多い場合は、メモリフットプリントに注目します。

0
tzl

Swift 4の回答を更新

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
        if touch.view != yourView
        {
            return false
        }

        return true
    }

その前に、オブジェクトにデリゲートプロパティを設定していることを確認してください。

0
abdul sathar