How to Remove All Gesture Recognizers from a Uiview in Swift

How to remove all gesture recognizers from a UIView in Swift

UPDATE FOR iOS 11

In general it is (and has always been) a bad idea to remove all gesture recognizes from a view by looping through its gestureRecognizers array. You should only remove gesture recognizers that you add to the view, by keeping track of those recognizers in your own instance variable.

This takes on new importance in iOS 11 for views that are involved in drag and drop, because UIKit adds its own gesture recognizers to those views to recognize drags and drops.

UPDATE

You no longer need to cast to UIGestureRecognizer, because UIView.gestureRecognizers was changed to type [UIGestureRecognizer]? in iOS 9.0.

Also, by using the nil-coalescing operator ??, you can avoid the if statement.

for recognizer in subview.gestureRecognizers ?? [] {
subview.removeGestureRecognizer(recognizer)
}

However, the shortest way to do it is this:

subview.gestureRecognizers?.forEach(subview.removeGestureRecognizer)

We can also do the filtering of the subviews in a for loop like this:

for subview in subviews where subview is CustomSubview {
for recognizer in subview.gestureRecognizers ?? [] {
subview.removeGestureRecognizer(recognizer)
}
}

Or we can wrap it all up into one expression (wrapped for clarity):

subviews.lazy.filter { $0 is CustomSubview }
.flatMap { $0.gestureRecognizers ?? [] }
.forEach { $0.view?.removeGestureRecognizer($0) }

The use of .lazy should prevent it from creating unnecessary temporary arrays.

ORIGINAL

This is one of those annoying things about Swift. Your for loop would just work in Objective-C, but in Swift you have to explicitly unwrap the optional array:

if let recognizers = subview.gestureRecognizers {
for recognizer in recognizers {
subview.removeGestureRecognizer(recognizer as! UIGestureRecognizer)
}
}

You could force-unwrap it (for recognizer in subview.gestureRecognizers!), but I'm not sure whether gestureRecognizers can return nil and you'll get a runtime error if it does and you force-unwrap it.

How to remove gesture recogniser

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

Swift - How to remove swipe gesture from scene when moving to another one?

The following removes all swipe gesture recognizers from the view:

override func willMoveFromView(view: SKView) {
if let gestures = view.gestureRecognizers {
for gesture in gestures {
if let recognizer = gesture as? UISwipeGestureRecognizer {
view.removeGestureRecognizer(recognizer)
}
}
}
}

Disable GestureRecognizer

gestureRecognizers is an array that contains all gesture recognizers attached to the view. You should loop through all gesture recognizers in that array and set their enabled property to false like this:

for (UIGestureRecognizer * g in imageViewGurka1.gestureRecognizers) {
g.enabled = NO;
}

Swift 5

Below the equivalent for Swift 5

for gesture in imageViewGurka1.gestureRecognizers! {
gesture.isEnabled = false
}

How to disable one of multiple gesture recognizers?

You should assign the name property to your gesture recognizer.

tap.name = "myTapGesture"

And later you can cycle through the recozniers and only disable the one that you want.

for aRecognizer in view.gestureRecognizers {
if let name = aRecognizer.name {
if name == "myTapGesture" {
aRecognizer.isEnabled = false
}
}
}

Disable gesture recognizer only for a particular view

I have did this,with help of

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event

and in that i have checked for touch point location & according to touch location i did my work like this

if(points.x>86)
{//hide the side panel
}

It recognizes gestures with synchronize with events.

Remove all UITextView's Gesture Recognizers

Here's how we handle UITextView user interaction that is contained within a UITableViewCell:

1) Your UIViewController should conform to UITableViewDataSource, UITableViewDelegate and UITextViewDelegate :

#import <UIKit/UIKit.h>

@interace MyExampleController : UIViewController <UITableViewDataSource, UITableViewDelegate, UITextViewDelegate>

2) Initially, the text view's userInteractionEnabled property is set to NO

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *textViewCellIdentifier = @"MyTextViewCellIdentifier";
MyTextViewViewCell *cell = [tableView dequeueReusableCellWithIdentifier:textViewCellIdentifier];

if (!cell)
{
// ... do your stuff to create the cell...
cell.textView.userInteractionEnabled = NO;
cell.textView.delegate = self;
}

// do whatever else to set the cell text, etc you need...

return cell;
}

3) Check if a text view cell was tapped via UITableViewDelegate method:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
BOOL isTextViewCell = ... // do your check here to determine if this cell has a text view

if (isTextViewCell)
{
[[(MyTextTableViewCell *)cell textView] setUserInteractionEnabled:YES];
[[(MyTextTableViewCell *)cell textView] becomeFirstResponder];
}
else
{
// ... do whatever else you do...
}
}

4) Check for \n to determine when to have the textView resign first responder (passed when user presses the return key):

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
if ([text rangeOfString:@"\n"].location != NSNotFound)
{
[textView resignFirstResponder];
textView.
return NO;
}

return YES;
}

5) Save text into your model once after the text view has resigned (ends editing):

- (void)textViewDidEndEditing:(UITextView *)textView
{
NSString *text = textView.text;
// do your saving here
}

This was mostly written on the spot, so there may be some minor errors in there, but hopefully you get the general idea.

Good luck.

Is it recommended to remove gesture recogniser manually in iOS apps

If you are NOT talking about using Xamarin then:

No you don't, the answer on the second link you posted is right. The first link is talking about Xamarin, same rules don't apply.

This is how you attach a gesture recognizer.
https://developer.apple.com/documentation/uikit/uiview/1622496-addgesturerecognizer

Under the "Discussion" part you can see this statement:

The view establishes a strong reference to the gesture recognizer.

Whenever you see this kind of statements it can be implied that "This object will keep my added object alive since it will strongly reference it". Thus, once the object disappears my added object will go with it.



Related Topics



Leave a reply



Submit