web-dev-qa-db-ja.com

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

私はこれがどのように行われるかを理解しようとしています正しい方法。私は状況を描写しようとしました: enter image description here

UITableViewのサブビューとしてUIViewを追加しています。 UIViewはタップとpinchGestureRecognizerに応答しますが、そうすると、tableviewはこれら2つのジェスチャに反応しなくなります(スワイプに反応します)。

次のコードで動作するようにしましたが、明らかにニースのソリューションではなく、より良い方法があると確信しています。これは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];
}
81
andershqst

私は非常に似た問題を抱えていて、私の解決策を見つけました in SO question 。要約すると、自分をUIGestureRecognizerのデリゲートとして設定し、許可する前にターゲットビューを確認しますタッチを処理する認識エンジン関連するデリゲートメソッドは次のとおりです。

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

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

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

独自のテーブルビューを持つドロップダウンサブビューを表示していました。その結果、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;
}
5
joslinm

1つの可能性は、ジェスチャー認識機能をサブクラス化して(まだ行っていない場合)、-touchesBegan:withEvent:除外されたサブビューで各タッチが開始されたかどうかを判断し、-ignoreTouch:forEvent:タッチした場合。

もちろん、除外されたサブビューを追跡するためのプロパティを追加する必要があります。除外されたサブビューの配列を追加することもできます。

4
Caleb

@Pin Shih Wangでの構築 answer 。タップジェスチャ認識機能を含むビュー上のタップ以外のすべてのタップは無視されます。すべてのタップは、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)
    }
}
4
Nick Ager

クラスを継承せずに実行することもできます。

ジェスチャーのコールバックセレクターで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
    }
}
2
Pin Shih Wang

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

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

}

Uは、子をトリガーするが親のレコグナイザーはトリガーしないようにする場合、failメソッドを使用できます。

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

1
user7270881

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

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

1

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

これは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
}

これにより、タップが無効になります。タップをインスタンス変数に変え、deallocで解放しなければなりませんでした。

0
j2emanue

私もポップオーバーをやっていて、これが私がやった方法です

func didTap(sender: UITapGestureRecognizer) {

    let tapLocation = sender.locationInView(tableView)

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