9

数年間、スーパービューとそのサブビューの両方にジェスチャ認識機能がある場合、サブビューが最初にタッチを受け取り、スーパービューのジェスチャをキャンセルするという仮定の下で操作してきました。iOS 7 までは、この仮定が裏切られることはなく、スーパービューのジェスチャーが干渉しないという確信を持って、サブビューにジェスチャーを追加することができました。しかし、iOS 7 では、最初にスーパービューがランダムにタッチを受け取り、サブビューのジェスチャをキャンセルします。これはめったに発生しないため、問題を特定するのが困難でした。

UITapGestureRecognizer私が最初にこの問題を経験したのは、...を使用してタップできないボタンでした。通常、ボタンは機能しなくなるまで機能します。あなたの正気を疑うようにしました。それで私は自分自身を転がして、TapGestureRecognizerスーパービューのタップが時々サブビューのタップをキャンセルしていることを発見しました. 以前のバージョンの iOS ではこれが行われたことはありませんが、この動作が約束されていなかったのではないかと思います。

サブビューのジェスチャーは、スーパービューのジェスチャーをキャンセルすることになっていると思いました(デリゲートによって特に指定されていない限り)。これは間違っていますか、それともバグですか?

注意:この状況をどのように処理するかを尋ねているわけではありません。私の仮定が間違っているかどうか誰かが知っているかどうか尋ねています。私はすでにビューを再配置し、ジェスチャーを動的に追加/削除しgestureRecognizer:shouldReceiveTouch:、状況を改善するためにかなり複雑な実装を作成しています。楽しいことではありませんが、問題を回避できます。

4

1 に答える 1

8

多くの検索を行った後、Apple のメッセージ ボードで、この問題を抱えている他のユーザーとのディスカッションを見つけました: UITapGestureRecognizer の問題(開発者アカウントが必要です)。私は先に進み、バグレポートを提出しました: 15331126 (バグレポートにリンクする方法を知っている人はいますか?) . その間、この回避策を実装しました。これまでのところ動作しているようですが、バグは非常にまれであるため、まだトリガーしていない可能性があります。私はそれをベータ版ユーザーにリリースしています。彼らから苦情がなければ (苦情を言っている)、これで問題が解決したと思います。

更新: このソリューションは問題を修正しました。何十人ものユーザーが数週間使用した後、ジェスチャに関する問題は 1 つもありませんでした。

私のジェスチャーのほとんどはカスタムです。それらを自分自身のデリゲートに変更して実装しました。

- (BOOL) gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
    if (gestureRecognizer == self){
        if ([otherGestureRecognizer isMemberOfClass:self.class]){
            if ([self isGestureRecognizerInSuperviewHierarchy:otherGestureRecognizer]){
                return YES;
            } else if ([self isGestureRecognizerInSiblings:otherGestureRecognizer]){
                return YES;
            }
        }
    }
    return NO;
}

私のカスタムのgestureRecognizersは現在、UIGestureRecognizerDelegateプロトコルを実装していることに注意してください(理由は後述します)。また、UIGestureRecognizer (上記のコードで使用) にいくつかのカテゴリを追加しました。

- (BOOL) isGestureRecognizerInSiblings:(UIGestureRecognizer *)recognizer{
    UIView *superview = self.view.superview;
    NSUInteger index = [superview.subviews indexOfObject:self.view];
    if (index != NSNotFound){
    for (int i = 0; i < index; i++){
            UIView *sibling = superview.subviews[i];
            for (UIGestureRecognizer *viewRecognizer in sibling.gestureRecognizers){
                if (recognizer == viewRecognizer){
                    return YES;
                }
            }
        }
    }
    return NO;
}
- (BOOL) isGestureRecognizerInSuperviewHierarchy:(UIGestureRecognizer *)recognizer{
    if (!recognizer) return NO;
    if (!self.view) return NO;
    //Check siblings
    UIView *superview = self.view;
    while (YES) {
        superview = superview.superview;
        if (!superview) return NO;
        for (UIGestureRecognizer *viewRecognizer in superview.gestureRecognizers){
            if (recognizer == viewRecognizer){
                return YES;
            }
        }
    }
}

スーパービュー ジェスチャで問題が発生するのを見ただけなので、兄弟を確認する必要があるかどうかは完全にはわかりません。しかし、私はそのチャンスを逃したくありませんでした。現在のビューの「上」にあるビュー ジェスチャをキャンセルしたくないため、現在のビューの「下」にある兄弟のみをチェックすることに注意してください。

カスタム認識エンジンのデリゲートとして自分自身を設定するクラスの実装を追加する必要がありましたが、ほとんどの場合、それらのクラスはジェスチャレコグナイザーにコールバックするだけです。

- (BOOL) gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
    if ([gestureRecognizer respondsToSelector:@selector(gestureRecognizer:shouldBeRequiredToFailByGestureRecognizer:)]){
        return [(id <UIGestureRecognizerDelegate>)gestureRecognizer gestureRecognizer:gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:otherGestureRecognizer];
    }
    return NO;
}

これが問題を抱えている他の誰かに役立つことを願っています。

于 2013-10-29T13:40:19.143 に答える