Tap Gesture Not Working as Expected When Added to Uiview in Collectionview Cell

Tap Gesture not working as expected when added to uiview in collectionview cell

The problem is that in your definition of tap property, self is not the instance of the custom cell class because at the time the property is created, the object hasn't been fully initialized.

If you add:

print(type(of: self))

to that code, you will see that it prints:

(CustomCell) -> () -> CustomCell

instead of the desired:

CustomCell

So your target/action is using the wrong target.

An easy way to fix this is to make tap a lazy var:

lazy var tap: UITapGestureRecognizer = {
let t = UITapGestureRecognizer(target: self, action: #selector(tapped))
return t
}()

Then, the first time you access tap, the tap gesture recognizer will be created and at that time, your custom cell will be created and self will refer to the instance of the class.


Alternatively, you can make tap a computed property:

var tap: UITapGestureRecognizer {
let t = UITapGestureRecognizer(target: self, action: #selector(tapped))
return t
}

and tap will create and return a UITapGestureRecognizer when it is accessed. Again, in that case, the custom cell will be created, so self will properly refer to the instance of the class.

TapGesture inside collectionView

You can try delaysTouchesBegan = true on the gesture recognizer. This should delay the touch processing that leads to didSelect and run your action from the gesture recognizer

How to add tap gesture to UICollectionView , while maintaining cell selection?

Whenever you want to add a gesture recognizer, but not steal the touches from the target view, you should set UIGestureRecognizer.cancelsTouchesInView for your gestureRecognizer instance to false.

collectionView didn't call didSelectItemAtIndexPath when superview has gesture

You need to set tapGesture.cancelsTouchesInView = NO.

Depending on Your logic You may want to check out delaysTouchesBegan too.

From Apple docs:

When the value of this property is false (the default), views analyze
touch events in began and moved in parallel with the receiver. When
the value of the property is true, the window suspends delivery of
touch objects in the UITouchPhaseBegan phase to the view. If the
gesture recognizer subsequently recognizes its gesture, these touch
objects are discarded. If the gesture recognizer, however, does not
recognize its gesture, the window delivers these objects to the view
in a touchesBegan(:with:) message (and possibly a follow-up
touchesMoved(
:with:) message to inform it of the touches’ current
locations). Set this property to true to prevent views from processing
any touches in the UITouchPhaseBegan phase that may be recognized as
part of this gesture.

EDIT :
For completeness I am adding code snippet for filtering the gesture recognizer's handling, when the user taps in on the collection view. My approach is different from the one mentioned in @DonMag's answer.

- (void)doGesture:(UIGestureRecognizer*) sender
{
CGPoint locationInView = [sender locationOfTouch:0 inView:self.view];
CGPojnt convertedLocation = [self.collectionView convertPoint:location fromView:self.view];

// from Apple doc
// Returns a Boolean value indicating whether the receiver contains the specified point.
if (![self.collectionView pointInside:convertedLocation withEvent:nil])
{
NSLog(@"%@",@"doGesture");
}
}

EDIT 2:
Maybe the clearest explanation about gesture recognizers and how they work, when added in views:

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. When a user touches that view, the
gesture recognizer receives a message that a touch occurred before the
view object does. As a result, the gesture recognizer can respond to
touches on behalf of the view.

Gesture Recognizer not responding to tap

You probably don't have/lost the connection to the @IBAction. You need to connect it once again. Find the UITapGestureRecognizer from the storyboard and control drag to the UIViewController subclass and create an action again, add the code there and delete the old method.

  1. Find tap gesture in storyboard and control-drag from it to UIViewController.

Tap gesture


  1. Create the @IBAction in the ViewController

@IBAction

You could also do it programmatically. Here's how:

override func viewDidLoad() {
super.viewDidLoad()

tappableView.isUserInteractionEnabled = true
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(selectImageFromPhotoLibrary(_:)))
tappableView.addGestureRecognizer(tapGesture)
}


Related Topics



Leave a reply



Submit