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

In the above code only taps on view2 are recognized. If I comment out the third line then taps on view1 are recognized. If I'm right and you can only use a gesture recognizer once, I'm not sure if this is a bug or it just needs some more documentation.

Srikar Appalaraju
  • 71,928
  • 54
  • 216
  • 264
kubi
  • 48,104
  • 19
  • 94
  • 118

13 Answers13

366

A UIGestureRecognizer is to be used with a single view. I agree the documentation is spotty. That UIGestureRecognizer has a single view property gives it away:

view

The view the gesture recognizer is attached to. (read-only)

@property(nonatomic, readonly) UIView *view

Discussion You attach (or add) a gesture recognizer to a UIView object using the addGestureRecognizer: method.

Community
  • 1
  • 1
TomSwift
  • 39,369
  • 12
  • 121
  • 149
  • 12
    Because adding a gesture recognizer to a view happens at runtime (vs. compile time). – TomSwift Aug 15 '14 at 21:20
  • 1
    I got that, but much like detecting that say we haven't used a variable, XCode could tell based on the code that we have passed the same recognizer to multiple views and could warn the coder. – Zoltán Matók Aug 18 '14 at 11:52
  • 1
    The compiler warning about multiple views assigning the same UITapGestureRecognizer is nonsense, because you may want to do this on purpose, for example if you want to move the tap gesture recognizer around from one view to another. That said, it's a silly limitation that the gesture recognizer cannot be used on multiple views. – Erik van der Neut Aug 27 '15 at 07:35
  • 1
    iOS 9 now enforces a single view per gesture recogniser, I'd been using the interface builder method below, but now I get the following message when I try to use it (some details cut for brevity): WARNING: A Gesture recognizer () was setup in a storyboard/xib to be added to more than one view (->; layer = >) at a time, this was never allowed, and is now enforced. Beginning with iOS 9.0 it will be put in the first view it is loaded into. – George Brown Jan 05 '16 at 21:54
  • If you're adding to view at second time, the view was attached before that by this recognizer is getting unattached automatically `UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didPressed:)]; [self.view1 addGestureRecognizer:tapRecognizer]; [self.view2 addGestureRecognizer:tapRecognizer];` Output _view1 has no gesture recognizers array ; view2 has got gesture recognizers array_ – kokos8998 Jun 07 '17 at 10:54
49

I got around it by using the below.

for (UIButton *aButton in myButtons) {

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

}

Then in my handleLongPress method I just set a UIButton equal to the view of the gesture recognizer and branch what I do based upon that button

- (void)handleLongPress:(UILongPressGestureRecognizer*)gesture {
    if ( gesture.state == UIGestureRecognizerStateEnded ) {
        UIButton *whichButton=(UIButton *)[gesture view];
        selectedButton=(UIButton *)[gesture view];
    ....
}
kwalker
  • 499
  • 4
  • 2
  • 1
    Great answer.Thanks a ton. This could have been the accepted answer if the question was "How you attach a UIGestureRecognizer to multiple views?" – DD_ Feb 15 '13 at 09:33
  • 8
    This (or something very close to this) did not work for me. I added several views to a tap gesture recognizer in Interface Builder, and connected the recognizer to an action. The action was called anytime an attached view was tapped, but gesture.view was always the last view attached. – Aneil Mallavarapu Mar 08 '13 at 06:02
  • This is really nice answer and also really helpful and agree with @MicRO +1 – Dilip Manek Jul 30 '13 at 13:16
  • 3
    Aneil, that's because you didn't create new instances of the gesture recognizer. What's happening in the loop in this answer here is that new instances of gesture recognizers are created, each with only one view attached. They can all point to the same handler, where you then check the view to see which one was touched. – Erik van der Neut Aug 27 '15 at 07:37
  • 1
    Can someone else confirm that this no longer works in the current version of Obj-C / Swift? – Maxi Mus Apr 12 '16 at 11:05
  • This is not much of a workaround as it is a loop that saves a few extra lines of code. All this is is adding multiple gestures that have call the same selector. In the end each view has its own instance of a new gesture recogniser. The sender that calls the selector is different when calling. – Vlad Apr 19 '16 at 00:55
18

For Swift 3 in case anyone requires this: Based on Bhavik Rathod Answer above.

 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())
George Asda
  • 2,119
  • 2
  • 28
  • 54
  • 3
    that's basically creating multiple gestures for the two views, still same rule: every gesture has only one view to be attached to – Abdoelrhman Jan 26 '17 at 15:14
  • 3
    No, the function create a gesture every time it's called – Abdoelrhman Jan 27 '17 at 13:10
  • 2
    the name of the function is incorrect. the logical function here is a getting function. so it should be named: `getGestureRecognize` because that is what this function does – David Seek May 30 '18 at 22:27
  • Work nice for me! And code more clean than create multiple variables or put whole code for creation inside addGestureRecognizer – Codenator81 Jul 11 '18 at 10:40
14

No you should not attach gesture recognizers to more than one view.

There is this explicit information in the Apple documentation:

Gesture Recognizers Are Attached to a View

Every gesture recognizer is associated with one view. By contrast, a view can have multiple gesture recognizers, because a single view might respond to many different gestures. For a gesture recognizer to recognize touches that occur in a particular view, you must attach the gesture recognizer to that view.

Event Handling Guide for iOS - Gesture Recognizers Apple Developer Library

While as others mention they might work in some cases it is clearly against the documentation and could change in any future iOS version.

What you can do is add separate gesture recognisers to the views you want to monitor and they can share a common action.

Joseph Lord
  • 6,446
  • 1
  • 28
  • 32
12

We can do something Like this, it's easy and simple

1) create function as below in your controller (this function will return GestureRecognizer)

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

2) now set this recognizer in multiple views

[self.view1 addGestureRecognizer:[self setRecognizer]]; 
[self.view2 addGestureRecognizer:[self setRecognizer]];
rathodbhavikk
  • 416
  • 7
  • 20
  • It's not working for me when I use two labels instead of views. – Mihir Oza May 30 '18 at 05:39
  • 3
    @Mihir Oza, it can't work for UILabels directly. Because of labels doesn't have sense for user interaction. If you want to add gestures for UILabels add this single line **labelName..isUserInteractionEnabled = true** in Swift. Then add gestures. – Naresh May 20 '19 at 11:05
  • It's too late man, I have already fixed that. But Thanks for the suggestion. Your comment will be helpful for stack users. Appreciated! – Mihir Oza May 22 '19 at 05:35
  • 1
    I guess the line `setNumberOfTapsRequired:1` is not necessary – Naveed Abbas Jul 12 '19 at 05:56
4

Well if someone does not want to code for adding gesture view for multiple buttons like kwalker has answered above, and want to do it via Interface Builder this may help you.

1) You can add Long Press gesture Recognizer from Object Library like you add other objects like UIButtons and UILabels.

enter image description here Initially what I ended up using was I took only one

2) Set referencing outlets to UIButton and sent actions with File's Owner.

enter image description here

Note: If you have multiple UIButton or any other object you will need separate gesture recognizer for each of them. For more details please refer to this question of mine.Getting wrong UIButton tag on Long press gesture recognizer

Community
  • 1
  • 1
rohan-patel
  • 5,772
  • 5
  • 45
  • 68
  • It's very easy to bind more than one UIView to guesture recognizer using IB. The question was about code generation. – AlexeyVMP Feb 01 '15 at 10:55
3

if you have fixed view I suggest you doing something like this

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

that way will reduce multiple different useless variable

3

You could create a generic extension on view to add gesture recognizers easily. This is just an example but it could look like this

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)
    }
  }

}

To add a 2 tap recognizer on a view you would just call:

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

You could also easily add a swipe recognizer

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

and so on. Just remember that the target must be linked to the selector.

Martin
  • 843
  • 8
  • 17
2

Override class by '<UIScrollViewDelegate>'

And use this method in .m class:

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

This method will help you to enable multiple swipe on a single view..

AnkitRox
  • 534
  • 1
  • 6
  • 16
2

What about re write (recreate) your GestureRecognize every time that you add a gesture recognizer pointing to the same func. In below case it works. I am using IBOutletCollection

Swift 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")
}
febaisi
  • 644
  • 6
  • 15
1

I know this is an old post but I figured something similar and hopefully it's useful someone else. I simply stored my imageViews in an array and assigned it to to the same gesture recognizer in a function to set up each image view.

In my viewDidLoad():

imageViewList = [imageView, imageView2, imageView3]
setupImageViews(imageViews: imageViewList)

Function to setup image views:

func setupImageViews(imageViews: [UIImageView]) {
    
    for imageView in imageViews {
        let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(imageTapped(tapGestureRecognizer:)))
        imageView.isUserInteractionEnabled = true
        imageView.addGestureRecognizer(tapGestureRecognizer)
        
        //set up image to be displayed with the right aspect
        imageView.autoresizingMask = [.flexibleWidth, .flexibleHeight, .flexibleBottomMargin, .flexibleRightMargin, .flexibleLeftMargin, .flexibleTopMargin]
        imageView.contentMode = .scaleAspectFit // OR .scaleAspectFill
        imageView.clipsToBounds = true
    }
}

And in the action selector imageTapped(), you can have corresponding code for whichever image view tapped.

@objc func imageTapped(tapGestureRecognizer: UITapGestureRecognizer)
{
    switch tapGestureRecognizer.view {
    case imageView:
        print("tapped Image View 1") //add your actions here
    case imageView2:
        print("tapped Image View 2") //add your actions here
    case imageView3:
        print("tapped Image View 3") //add your actions here
    default:
        print("Tap not detected")
    }
    _ = tapGestureRecognizer.view as! UIImageView
    //additional code...
}
RyanNa
  • 119
  • 1
  • 6
  • 2
    That's not the same gesture recognizer, though. You're initializing a new one for each `UIImageView` in your example. – duraz0rz Oct 07 '20 at 20:49
  • And when I move the initialization of the tap gesture recognizer our of the loop so that there is only one I get no errors, but the gesture recognizer is only really added to the last view in the array and the first 3 (in my case) do nothing on tap. – Dominick Mar 15 '21 at 19:46
  • you are creating new instance of UITapGestureRecognizer. – Akhtar Jul 27 '22 at 14:39
-1

To add a tap gesture to multiple views in Swift, you can use a loop to iterate through the views and add the gesture recognizer to each one. Here's an example:

let views = [view1, view2, view3, view4]
        views.forEach { view in
            let tap = UITapGestureRecognizer(target: self, action: #selector(self.handleTap(_:)))
            view.isUserInteractionEnabled = true
            view.addGestureRecognizer(tap)
        }
    }
    
    @objc func handleTap(_ sender: UITapGestureRecognizer? = nil) {
        switch sender?.view {
        case view1:
            //do something
        case view2:
            //do something
        default:
            break
        }
    }
Naqeeb
  • 1,121
  • 8
  • 25
-7

You can do it using this code my views which are imageviews in the xib.

- (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;
        }
    }
}
Dilip Manek
  • 9,095
  • 5
  • 44
  • 56
  • 1
    You're creating multiple gesture recognizers; my original question was about reusing a single gesture recognizer, which you cannot do. – kubi Sep 24 '13 at 22:51
  • 1
    What is the point of adding `500` to all of your views' tags and then subtracting `500`? Why not just start your tags at `1` (or even `0`) instead of `501`? – ma11hew28 Oct 29 '13 at 15:26
  • @MattDiPasquale, Does Not matter if you want to start with the `1` its just i have copied this code from my app where i am giving it from `501`. But yes don't give `0` bcoz i have read somewhere that its always indicate parent-view so it will create complication, Believe me i have faced it. – Dilip Manek Oct 29 '13 at 15:31
  • The key text in the documentation is "The view establishes a strong reference to the gesture recognizer." which means that the view owns the gesture. The gesture can only have one owner. See [link](https://developer.apple.com/reference/uikit/uiview/1622496-addgesturerecognizer) – Phantom59 Oct 14 '16 at 01:55