UIGestureRecognizerを複数のビューにアタッチできますか?


228
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapTapTap:)];
[self.view1 addGestureRecognizer:tapGesture];
[self.view2 addGestureRecognizer:tapGesture];
[tapGesture release];

上記のコードではタップのみview2が認識されます。3行目をコメント化すると、タップview1が認識されます。私が正しく、ジェスチャー認識機能を1回しか使用できない場合、これがバグなのか、それともいくつかのドキュメントが必要なだけなのかわかりません。

回答:


334

A UIGestureRecognizerは単一のビューで使用されます。ドキュメントがむらがあることに同意します。それUIGestureRecognizerはそれをview与える単一のプロパティを持っています:

見る

ジェスチャレコグナイザがアタッチされているビュー。(読み取り専用)

@property(nonatomic、readonly)UIView *ビュー

ディスカッションaddGestureRecognizer:メソッドを使用して、ジェスチャレコグナイザをUIViewオブジェクトにアタッチ(または追加)します。


11
ジェスチャレコグナイザをビューに追加するのは、実行時(コンパイル時ではなく)に行われるためです。
TomSwift 2014

1
私はそれを手に入れましたが、変数を使用していないことを検出するのと同じように、XCodeはコードに基づいて、同じ認識機能を複数のビューに渡してコーダーに警告することを通知できます。
ゾルタンMatók

1
同じUITapGestureRecognizerを割り当てる複数のビューに関するコンパイラの警告は意味がありません。たとえば、あるビューから別のビューにタップジェスチャレコグナイザを移動したい場合など、意図的にこれを実行したい場合があるためです。とはいえ、ジェスチャ認識機能を複数のビューで使用できないのはばかげた制限です。
Erik van der Neut、2015

1
iOS 9では、ジェスチャー認識機能ごとに1つのビューが適用されるようになりました。以下のインターフェースビルダーメソッドを使用していましたが、使用しようとすると次のメッセージが表示されます(簡潔にするために一部詳細を省略):警告:ジェスチャー認識機能(< UITapGestureRecognizer:.....>)がストーリーボード/ xibで設定され、複数のビューに追加されました(-> <UIView:; frame =(0 44; 600 536); autoresize = RM + BM; GestureRecognizers = < NSArray ...:>; layer = <CALayer:... >>)一度に、これは許可されなかったため、強制されます。iOS 9.0以降、ロードされた最初のビューに配置されます。
ジョージブラウン

2回目にビューに追加する場合、ビューはこの認識機能によって自動的に接続解除される前に接続されました。UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didPressed:)]; [self.view1 addGestureRecognizer:tapRecognizer]; [self.view2 addGestureRecognizer:tapRecognizer];出力view1にはジェスチャー認識機能配列がありません。view2にはジェスチャー認識配列があります
kokos8998

48

以下を使って回避しました。

for (UIButton *aButton in myButtons) {

            UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
            longPress.minimumPressDuration=1.0;
            [aButton addGestureRecognizer:longPress];
            [longPress release];

}

次に、handleLongPressメソッドで、UIButtonをジェスチャレコグナイザのビューに等しく設定し、そのボタンに基づいて実行する処理を分岐します

- (void)handleLongPress:(UILongPressGestureRecognizer*)gesture {
    if ( gesture.state == UIGestureRecognizerStateEnded ) {
        UIButton *whichButton=(UIButton *)[gesture view];
        selectedButton=(UIButton *)[gesture view];
    ....
}

1
すばらしい回答です。ありがとうございました。質問が「UIGestureRecognizerを複数のビューにどのようにアタッチするのですか?」
D_D

7
これ(またはこれに非常に近いもの)は私にとってはうまくいきませんでした。Interface Builderのタップジェスチャレコグナイザにいくつかのビューを追加し、レコグナイザをアクションに接続しました。アタッチされたビューがタップされるたびにアクションが呼び出されましたが、ジェスチャー。ビューは常に最後にアタッチされたビューでした。
Aneil Mallavarapu 2013年

これは本当に良い回答であり、また非常に役に立ち、@ MicRO +1
Dilip

2
アニール、ジェスチャーレコグナイザーの新しいインスタンスを作成しなかったからです。この回答のループで起こっていることは、ジェスチャ認識機能の新しいインスタンスが作成され、それぞれにビューが1つだけアタッチされていることです。それらはすべて同じハンドラーを指すことができ、そこでビューをチェックしてどのハンドラーがタッチされたかを確認します。
Erik van der Neut 2015

1
他の誰かがこれが現在のバージョンのObj-C / Swiftで機能しないことを確認できますか?
Maxi Mus

18

誰かがこれを必要とする場合のSwift 3の場合:上記のBhavik Rathod Answerに基づく。

 func setGestureRecognizer() -> UIPanGestureRecognizer {

        var panRecognizer = UIPanGestureRecognizer()

        panRecognizer = UIPanGestureRecognizer (target: self, action: #selector(pan(panGesture:)))
        panRecognizer.minimumNumberOfTouches = 1
        panRecognizer.maximumNumberOfTouches = 1
        return panRecognizer
    }

        ///set the recognize in multiple views
        view1.addGestureRecognizer(setGestureRecognizer())
        view2.addGestureRecognizer(setGestureRecognizer())

3
基本的には、2つのビューに対して複数のジェスチャーを作成しますが、ルールは同じです。どのジェスチャーにも、アタッチするビューは1つだけです
Abdoelrhman

3
いいえ、関数は呼び出されるたびにジェスチャーを作成します
Abdoelrhman 2017年

2
関数の名前が正しくありません。ここでの論理関数は取得関数です。したがって、名前を付ける必要があります。getGestureRecognizeそれは、この関数が行うことです
David Seek

私のためにうまくいきます!そして、複数の変数を作成するよりもクリーンなコードを作成するか、コード全体をaddGestureRecognizer内に作成します
Codenator81

11

このようにできるのは簡単でシンプルです

1)コントローラで以下のように関数を作成します(この関数はGestureRecognizerを返します)

-(UITapGestureRecognizer*)setRecognizer{
     UITapGestureRecognizer *gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(openProfile)];
     [gestureRecognizer setNumberOfTapsRequired:1];
     return gestureRecognizer;
}

2)複数のビューでこの認識機能を設定する

[self.view1 addGestureRecognizer:[self setRecognizer]]; 
[self.view2 addGestureRecognizer:[self setRecognizer]];

ビューの代わりに2つのラベルを使用すると、うまくいきません。
Mihir Oza 2018

3
@Mihir Oza、UILabelsには直接機能しません。なぜなら、ラベルはユーザーとの対話には意味がないからです。UILabelsのジェスチャーを追加する場合は、Swiftでこの1行のlabelName..isUserInteractionEnabled = trueを追加します。次に、ジェスチャーを追加します。
iOS

手遅れです、私はすでにそれを修正しました。しかし、提案をありがとう。あなたのコメントはスタックユーザーに役立ちます。感謝!
Mihir Oza

1
setNumberOfTapsRequired:1は必要ないと思います
Naveed Abbas

9

いいえ、ジェスチャレコグナイザを複数のビューにアタッチしないでください。

Appleのドキュメントには、この明示的な情報があります。

ジェスチャーレコグナイザーがビューにアタッチされている

すべてのジェスチャー認識機能は1つのビューに関連付けられています。対照的に、単一のビューが多くの異なるジェスチャーに応答する可能性があるため、ビューは複数のジェスチャー認識機能を持つことができます。ジェスチャレコグナイザが特定のビューで発生するタッチを認識するには、そのビューにジェスチャレコグナイザを接続する必要があります。

iOS向けイベント処理ガイド-ジェスチャーレコグナイザー Apple Developer Library

他の人が言及するように、それらはいくつかのケースで機能するかもしれませんが、それは明らかにドキュメントに反しており、将来のiOSバージョンで変更される可能性があります。

できることは、監視するビューに個別のジェスチャー認識機能を追加することであり、それらは共通のアクションを共有できます。


4

まあ、kwalkerが上記で答えたように、複数のボタンにジェスチャービューを追加するためのコードを作成したくない場合や、Interface Builderを介してそれを実行したい場合は、これが役立つことがあります。

1)UIButtonsやUILabelsのような他のオブジェクトを追加するように、オブジェクトライブラリから長押しジェスチャーレコグナイザーを追加できます。

ここに画像の説明を入力してください 最初に使用したのは1つだけだった

2)UIButtonファイルの所有者に参照アウトレットを設定し、アクションを送信します。

ここに画像の説明を入力してください

注:複数のUIButtonまたは他のオブジェクトがある場合は、それぞれに個別のジェスチャー認識機能が必要になります。詳細については、この私の質問を参照してください。長押しジェスチャー認識機能で間違ったUIButtonタグを取得する


IBを使用して複数のUIViewをゲスト認識エンジンにバインドするのは非常に簡単です。問題はコード生成についてでした。
AlexeyVMP 2015

3

固定ビューがある場合、私はこのようなことをすることをお勧めします

[self.view1 addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapTapTap:)]];
[self.view2 addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapTapTap:)]];

その方法は複数の異なる役に立たない変数を減らすでしょう


3

ビューに汎用拡張機能を作成して、ジェスチャー認識機能を簡単に追加できます。これは単なる例ですが、次のようになります。

extension UIView {

    func setGestureRecognizer<Gesture: UIGestureRecognizer>(of type: Gesture.Type, target: Any, actionSelector: Selector, swipeDirection: UISwipeGestureRecognizer.Direction? = nil, numOfTaps: Int = 1) {
    let getRecognizer = type.init(target: target, action: actionSelector)

    switch getRecognizer {
    case let swipeGesture as UISwipeGestureRecognizer:
        guard let direction = swipeDirection else { return }
        swipeGesture.direction = direction
        self.addGestureRecognizer(swipeGesture)
    case let tapGesture as UITapGestureRecognizer:
        tapGesture.numberOfTapsRequired = numOfTaps
        self.addGestureRecognizer(tapGesture)
    default:
        self.addGestureRecognizer(getRecognizer)
    }
  }

}

呼び出すだけのビューに2タップ認識機能を追加するには、次のようにします。

let actionSelector = #selector(actionToExecute)
view.setGestureRecognizer(of: UITapGestureRecognizer.self, target: self, actionSelector: actionSelector, numOfTaps: 2)

スワイプ認識機能を簡単に追加することもできます

view.setGestureRecognizer(of: UISwipeGestureRecognizer.self, target: self, actionSelector: actionSelector, swipeDirection: .down)

等々。ターゲットはセレクターにリンクする必要があることを覚えておいてください。


2

<UIScrollViewDelegate>」でクラスをオーバーライド

そして、このメソッドを.mクラスで使用します:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
    return YES;
}

このメソッドは、単一のビューで複数のスワイプを有効にするのに役立ちます。


2

同じ関数を指すジェスチャーレコグナイザーを追加するたびに、GestureRecognizeを書き直す(再作成する)とはどうでしょう。以下のケースでは動作します。IBOutletCollectionを使用しています

スウィフト2:

@IBOutlet var topicView: [UIView]!

override func viewDidLoad() {
        for view in self.topicView as [UIView] {
        view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "viewClicked:"))
    }
}

func viewClicked(recognizer: UITapGestureRecognizer) {
    print("tap")
}

-6

あなたはこのコードを使用してそれを行うことができますxibのimageviewsである私のビュー。

- (void)viewDidLoad
{
    firstIV.tag = 501;
    secondIV.tag = 502;
    thirdIV.tag = 503;
    forthIV.tag = 504;

    [self addTapGesturetoImageView: firstIV];
    [self addTapGesturetoImageView: secondIV];
    [self addTapGesturetoImageView: thirdIV];
    [self addTapGesturetoImageView: forthIV];
}

-(void)addTapGesturetoImageView:(UIImageView*)iv
{
    iv.userInteractionEnabled = YES;
    UITapGestureRecognizer * textfielBGIVTapGasture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(textfielBGIVTapped:)];
    textfielBGIVTapGasture.numberOfTapsRequired = 1;
    [iv addGestureRecognizer:textfielBGIVTapGasture];
}

- (void)textfielBGIVTapped:(UITapGestureRecognizer *)recognizer {
    int tag = recognizer.view.tag-500;
    switch (tag) {
        case 1:
        {
            //firstIV tapped;
            break;
        }
        case 2:
        {
            //secondIV tapped;
            break;
        }
        case 3:
        {
            //thirdIV tapped;
            break;
        }
        case 4:
        {
            //forthIV tapped;
            break;
        }
        default: {
            break;
        }
    }
}

1
複数のジェスチャー認識機能を作成しています。私の最初の質問は、単一のジェスチャー認識機能を再利用することでしたが、それはできません。
kubi

1
500すべてのビューのタグに追加してから減算する意味は何500ですか?ではなく、タグを1(または0)から始めないのはなぜ501ですか?
ma11hew28 2013年

@MattDiPasquale、それから始めたいかどうかは関係ありません1私がこのコードを私のアプリからコピーしたところ、私がそれを与えてい501ます。しかし、はい、0私がどこかで読んだbcozに親ビューを常に示すことを与えないでください。それにより、複雑さを生み出します。私がそれに直面したと信じてください。
2013年

ドキュメントの重要なテキストは、「ビューはジェスチャ認識機能への強い参照を確立します」です。つまり、ビューはジェスチャーを所有しています。ジェスチャーは1人の所有者しか持てません。リンク
Phantom59 2016年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.