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.
- Find tap gesture in storyboard and control-drag from it to
UIViewController
.
- Create the
@IBAction
in theViewController
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
Notification Extension Access Core Data
How to Set Avaudioengine Input and Output Devices (Swift/Macos)
Protocol Can Only Be Used as a Generic Constraint
Swift: Generics and Type Constraints, Strange Behavior
Dictionary of String:Any Does Not Conform to Protocol 'Decodable'
How to Get Walking and Running Distance Using Healthkit in Swift
Swift 2 Error Handling and While
How to Apply Animation for One Specific Modifier Change Only
How to Decode the Body of an Error in Alamofire 5
How to Submit Swift 2.2 App with Xcode 7.3 When iOS 10 Is Released
Tap Gesture Not Working as Expected When Added to Uiview in Collectionview Cell
How to Stop/Cancel Playsoundfilenamed in Swift
Swiftui Editbutton Action on Done
Rotating Uitextview Programmatically
Property with '= {Return}()' or '{Return}'
Custom Annotation Showing Same Image for All Different Types of Poi'S