Can you attach a UIGestureRecognizer to multiple views?

IosObjective CUigesturerecognizer

Ios Problem Overview


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.

Ios Solutions


Solution 1 - Ios

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.

Solution 2 - Ios

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];
    ....
}

Solution 3 - Ios

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

Solution 4 - Ios

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]];

Solution 5 - Ios

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.

Solution 6 - Ios

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

  1. 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.https://stackoverflow.com/questions/15524683/getting-wrong-uibutton-tag-on-long-press-gesture-recognizer

Solution 7 - Ios

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

Solution 8 - Ios

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.

Solution 9 - Ios

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..

Solution 10 - Ios

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

Solution 11 - Ios

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

Solution 12 - Ios

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionkubiView Question on Stackoverflow
Solution 1 - IosTomSwiftView Answer on Stackoverflow
Solution 2 - IoskwalkerView Answer on Stackoverflow
Solution 3 - IosGeorge AsdaView Answer on Stackoverflow
Solution 4 - IosrathodbhavikkView Answer on Stackoverflow
Solution 5 - IosJoseph LordView Answer on Stackoverflow
Solution 6 - Iosrohan-patelView Answer on Stackoverflow
Solution 7 - IosRaynaldio LimargaView Answer on Stackoverflow
Solution 8 - IosMartinView Answer on Stackoverflow
Solution 9 - IosAnkitRoxView Answer on Stackoverflow
Solution 10 - IosfebaisiView Answer on Stackoverflow
Solution 11 - IosRyanNaView Answer on Stackoverflow
Solution 12 - IosDilipView Answer on Stackoverflow