UIGestureRecognizerは、タッチイベントを処理するためのサブビューをブロックします


82

私はこれがどのように正しい方法で行われるかを理解しようとしています。私は状況を描写しようとしました: ここに画像の説明を入力してください

UITableViewサブビューとしてを追加していUIViewます。UIViewTAP-に応答しpinchGestureRecognizerますが、その際、テーブルビューは、(それがまだ気の抜けたビールに反応)これら二つのジェスチャーに反応停止します。

私はそれを次のコードで動作させるようにしましたが、それは明らかに良い解決策ではなく、より良い方法があると確信しています。これはUIView(スーパービュー)に入れられます:

-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    if([super hitTest:point withEvent:event] == self) {
        for (id gesture in self.gestureRecognizers) {
            [gesture setEnabled:YES];
        }
        return self;
    }
    for (id gesture in self.gestureRecognizers) {
        [gesture setEnabled:NO];
    }
    return [self.subviews lastObject];
}

回答:


182

私は非常によく似た問題を抱えていて、このSOの質問で解決策を見つけました。要約すると、自分を代理人として設定し、レコグナイザーにUIGestureRecognizerタッチの処理を許可する前に、ターゲットビューを確認します。関連するデリゲートメソッドは次のとおりです。

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
       shouldReceiveTouch:(UITouch *)touch

3
私は、このソリューションのように、それのようにほとんどがタッチでいじり関与し、しませんhitTest:withEvent:pointInside:withEvent:
DarkDust 2011

6
たとえばreturn !(touch.view == givenView);、特定のビューを除外したい場合やreturn !(touch.view.tag == kTagNumReservedForExcludingViews);、認識機能がさまざまなサブビュー全体のタッチを処理するのを停止したい場合など、クリーンなソリューション 。
2012

6
でヒットテストを行い- (BOOL)isDescendantOfView:(UIView *)viewます。これは- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event、UIGestureRecognizerをサブクラス化するときにも正常に機能します。
クリストフ

1
@Justin、ジェスチャレコグナイザーがuiscrollviewに属していて、ジェスチャレコグナイザーを委任できない場合はどうなりますか?
–GökhanBarışAker 2014

108

サブビューへのタッチイベントのブロックは、デフォルトの動作です。この動作は変更できます。

UITapGestureRecognizer *r = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(agentPickerTapped:)];
r.cancelsTouchesInView = NO;
[agentPicker addGestureRecognizer:r];

5
これにより、タッチイベントがサブビューに送信されますが、ジェスチャ認識機能にも送信されます。したがって、これによりサブビューのブロックが防止されますが、ジェスチャ認識機能はサブビューで引き続き認識されます。
ジョナサン。

@ジョナサン。はい、あなたに賛成です。ジェスチャハンドラーメソッドで行ったことは、ジェスチャの場所が関連するサブビュー内で発生するかどうかを確認することです。その場合、コードの残りの部分を実行する必要はありません。また、この回避策を選択した理由の1つは、UITapGestureRecognizerがtranslationInViewメソッドを宣言していないことです。したがって、上記のUIGestureRecognizerDelegateメソッドを実装すると、エラーが発生してクラッシュするだけ... unrecognized selector sent to blahです。確認するには、次のようなものを使用しますCGRectContainsPoint(subview.bounds, [recognizer locationInView:subview])
MkVal 2014年

OPの質問によると、この回答をメインの回答として選択する必要があります。
farzadshbfn 2017

5

独自のテーブルビューを持つドロップダウンサブビューを表示していました。その結果、は、のtouch.viewようなクラスを返すことがありましたUITableViewCell。スーパークラスをステップスルーして、それが私が思っていたサブクラスであることを確認する必要がありました。

-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    UIView *view = touch.view;
    while (view.class != UIView.class) {
        // Check if superclass is of type dropdown
        if (view.class == dropDown.class) { // dropDown is an ivar; replace with your own
            NSLog(@"Is of type dropdown; returning NO");
            return NO;
        } else {
            view = view.superview;
        }
    }

    return YES;
}

whileループがそれを説明しています
joslinm 2016

5

@Pin ShihWangの回答に基づいて構築します。タップジェスチャレコグナイザーを含むビュー上のタップ以外のすべてのタップは無視されます。すべてのタップは、設定したとおりに通常どおりビュー階層に転送されtapGestureRecognizer.cancelsTouchesInView = falseます。Swift3 / 4のコードは次のとおりです。

func ensureBackgroundTapDismissesKeyboard() {
    let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap))
    tapGestureRecognizer.cancelsTouchesInView = false
    self.view.addGestureRecognizer(tapGestureRecognizer)
}

@objc func handleTap(recognizer: UIGestureRecognizer) {
    let location = recognizer.location(in: self.view)
    let hitTestView = self.view.hitTest(location, with: UIEvent())
    if hitTestView?.gestureRecognizers?.contains(recognizer) == .some(true) {
        // I dismiss the keyboard on a tap on the scroll view
        // REPLACE with own logic
        self.view.endEditing(true)
    }
}

ジェスチャレコグナイザーをと比較するにはどうすればよいUISwipeActionStandardButtonですか?私が試した.some(UISwipeActionStandardButton)
Jalil

4

1つの可能性は、ジェスチャ-touchesBegan:withEvent:レコグナイザをサブクラス化し(まだ行っていない場合)、各タッチが除外されたサブビューで開始されたかどうかを判断し、開始された-ignoreTouch:forEvent:場合はそのタッチを呼び出すようにオーバーライドすることです。

もちろん、除外されたサブビュー、またはおそらくより良い、除外されたサブビューの配列を追跡するために、プロパティを追加する必要もあります。


ここでのコードの山がありますgithub.com/...
johndpope

2

クラスを継承せずに行うことができます。

ジェスチャのコールバックセレクタでgestureRecognizersを確認できます

view.gestureRecognizersにgestureRecognizerが含まれていない場合は、無視してください

例えば

- (void)viewDidLoad
{
    UITapGestureRecognizer *singleTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self     action:@selector(handleSingleTap:)];
    singleTapGesture.numberOfTapsRequired = 1;
}

ここでview.gestureRecognizersを確認してください

- (void)handleSingleTap:(UIGestureRecognizer *)gestureRecognizer
{
    UIEvent *event = [[UIEvent alloc] init];
    CGPoint location = [gestureRecognizer locationInView:self.view];

    //check actually view you hit via hitTest
    UIView *view = [self.view hitTest:location withEvent:event];

    if ([view.gestureRecognizers containsObject:gestureRecognizer]) {
        //your UIView
        //do something
    }
    else {
        //your UITableView or some thing else...
        //ignore
    }
}

他の回答で述べたように、この方法を使用する場合は、タップジェスチャsingleTapGesture.cancelsTouchesInView = NO;viewDidLoad
Nick Ager 2017

1

特定のビューのスーパービューにアタッチされているすべてのジェスチャレコグナイザーをブロックするように設計されたUIGestureRecognizerサブクラスを作成しました。

それは私のWEPopoverプロジェクトの一部です。あなたはここでそれを見つけることができます。


1

parentViewのすべてのレコグナイザーにデリゲートを実装し、レコグナイザーの同時トリガーを担当するデリゲートにgestureRecognizerメソッドを配置します。

func gestureRecognizer(UIGestureRecognizer,       shouldBeRequiredToFailByGestureRecognizer:UIGestureRecognizer) -> Bool {
if (otherGestureRecognizer.view.isDescendantOfView(gestureRecognizer.view)) {
    return true
    } else {
    return false
}

}

子をトリガーさせたいが親認識機能をトリガーさせたくない場合は、failメソッドを使用できます。

https://developer.apple.com/reference/uikit/uigesturerecognizerdelegate


0

私もポップオーバーをしていました、そしてこれは私がそれをした方法です

func didTap(sender: UITapGestureRecognizer) {

    let tapLocation = sender.locationInView(tableView)

    if let _ = tableView.indexPathForRowAtPoint(tapLocation) {
        sender.cancelsTouchesInView = false
    }
    else {
        delegate?.menuDimissed()
    }
}

0

あなたはそれをオフにしてオンにすることができます....私のコードでは、キーボードが表示されていないときにそれをオフにする必要があるので、私はこのようなことをしました、あなたはそれをあなたの状況に適用することができます:

これをviewdidloadなどと呼びます。

NSNotificationCenter    *center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:@selector(notifyShowKeyboard:) name:UIKeyboardDidShowNotification object:nil];
[center addObserver:self selector:@selector(notifyHideKeyboard:) name:UIKeyboardWillHideNotification object:nil];

次に、2つのメソッドを作成します。

-(void) notifyShowKeyboard:(NSNotification *)inNotification 
{
    tap.enabled=true;  // turn the gesture on
}

-(void) notifyHideKeyboard:(NSNotification *)inNotification 
{
    tap.enabled=false;  //turn the gesture off so it wont consume the touch event
}

これにより、タップが無効になります。ただし、tapをインスタンス変数に変換してdeallocで解放する必要がありました。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.