How to remove gesture recogniser

2020-05-19 14:23发布

问题:

SO, I am adding a gesture recogniser to an overlay view. When tapped on screen i want this overlay to go away. Having said that adding a gesture recognizer overrides the "touch up inside" and other button click events. I need this back therefore i need to removegesturerecognizer. I can use this method however i have a problem. My code below -

- (void)helpClicked
{
    CGRect visibleBounds = [self convertRect:[self bounds] toView:viewContainer];
    CGFloat minimumVisibleX = CGRectGetMinX(visibleBounds);
    UIImageView * helpOverlay = [[UIImageView alloc]initWithFrame:CGRectMake(minimumVisibleX, 0, 1024, 768)];
    UIImage * helpImage = [UIImage imageNamed:@"HelpOverLay.png"];
    [helpOverlay setImage:helpImage];
    helpOverlay.tag = 50;
    self.scrollEnabled = NO;
    [self addSubview:helpOverlay]; 
    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] 
                               initWithTarget:self
                               action:@selector(dismissView)];

[self addGestureRecognizer:tap];    

}

Here i am taking the overlay off the other view.

- (void) dismissView
{
    UIView *overlay = [self viewWithTag:50];
    [overlay removeFromSuperview];
    self.scrollEnabled = YES;
}

My question is how do i remove the gesture recognizer in the second method? I cant pass the variable tap into this method nor can i remove it in the previous method either. Any pointers? Ive been stuck with quite a lot of passing variable problems when it comes to events.

回答1:

From the WWDC 2015, Cocoa Touch Best Practices, it is suggested that you keep a property or iVar if you need to access it later, and don't go with using viewWithTag:.

Moto: Properties instead of Tags

This saves you from some trouble:

  1. When dealing with multiple gestures, you remove the gesture that you want directly with accessing the property and remove it. (Without the need to iterate all the view's gestures to get the correct one to be removed)
  2. Finding the correct gesture by the tag when you are iterating, is very misleading when you have multiple tags on views, and when having conflicts with a specific tag

(i.e) You implemented it first time with tags, and everything works as expected. Later you work on another functionality which lets say breaks this and causes undesired behavior that you don't expect it. Log doesn't give you a warning, and the best thing you can get depending on the case it's a crash signalizing unrecognized selector sent to instance. Sometimes you won't get any of these.

Solution

Declare the iVar

@implementation YourController {
    UITapGestureRecognizer *tap;
}

Setup your view

- (void) helpClicked {
    //Your customization code

    //Adding tap gesture
    tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(dismissView)];
    [self addGestureRecognizer:tap];
}

Remove the gesture directly

- (void) dismissView {
    [self.view removeGestureRecognizer:tap];
}


回答2:

This loop will remove all gesture recognizers a view has

for (UIGestureRecognizer *recognizer in self.view.gestureRecognizers) {
    [self.view removeGestureRecognizer:recognizer];
}


回答3:

Declare an ivar UITapGestureRecognizer *tap in your @interface.

Change helpClicked to:

- (void)helpClicked
{
    CGRect visibleBounds = [self convertRect:[self bounds] toView:viewContainer];
    CGFloat minimumVisibleX = CGRectGetMinX(visibleBounds);
    UIImageView * helpOverlay = [[UIImageView alloc]initWithFrame:CGRectMake(minimumVisibleX, 0, 1024, 768)];
    UIImage * helpImage = [UIImage imageNamed:@"HelpOverLay.png"];
    [helpOverlay setImage:helpImage];
    helpOverlay.tag = 50;
    self.scrollEnabled = NO;
    [self addSubview:helpOverlay]; 
    tap = [[UITapGestureRecognizer alloc] 
                               initWithTarget:self
                               action:@selector(dismissView)];

    [self addGestureRecognizer:tap];  
}

and dismissView to:

for (UIGestureRecognizer *recognizer in self.view.gestureRecognizers) {
    [self removeGestureRecognizer:tap];
}

EDIT: i think nhahtdh's method is a bit more elegant compared to this one.

EDIT2: it seems you have [self addGestureRecognizer:tap] working so i'm asumming this is a subclass of UIView.



回答4:

Just set up the overlay view once, with the gesture recognizer set up, but make the overlay view hidden. When a view is hidden, it won't receive any touch from user. Only make the overlay view visible the view when necessary, and make it hidden when you don't need it.



回答5:

Your code should probably look more like this, for the second method:

- (void) dismissView {
  UIView *overlay = [self viewWithTag:50];

  for (UIGestureRecognizer *recognizer in self.view.gestureRecognizers) {
    if([recognizer isKindOfClass:[UITapGestureRecognizer class]]) {
      [self removeGestureRecognizer:recognizer];
    }
  }

  [overlay removeFromSuperview];
  self.scrollEnabled = YES;
}

I added the check for UITapGestureRecognizer, in case your class handles more than 1 UIGestureRecognizers and you only want to remove that.



回答6:

Swift version:

if let recognizers = yourView.gestureRecognizers { 
  for recognizer in recognizers {
    yourView.removeGestureRecognizer(recognizer)
  }
}


回答7:

This work for me:

for (UIGestureRecognizer *gr in self.view.gestureRecognizers) {
  [self.view removeGestureRecognizer:gr];
}


回答8:

If you are able to extend the view you could try this way:

_ = gestureRecognizers.flatMap { $0.map { removeGestureRecognizer($0) } }


回答9:

In Swift 4

if let gestures = shotButton.gestureRecognizers //first be safe if gestures are there
    {
        for gesture in gestures //get one by one
        {
            shotButton.removeGestureRecognizer(gesture) //remove gesture one by one
        }
    }


回答10:

It worked for me.

while (view.gestureRecognizers.count) {
  [view removeGestureRecognizer:[view.gestureRecognizers objectAtIndex:0]];
}