How to pass pan gesture to UICollectionVIew from UICollectionViewCell?
Solution 1
Instead of attaching a UIPanGestureRecognizer
to each cell (which will decrease performance) add a UIPanGestureRecognizer
to the UICollectionView
and when the pan gesture happens use locationInView
to get the point in the UICollectionView
where the pan started, and then indexPathForItemAtPoint
which will return you the index path for the cell you should animate.
This way, you will have only one gesture recognizer (good!) for your whole collection view while also maintaining the control in your view controller (as you wanted) - double win!
Using this solution, in your view controller you would implement gestureRecognizer:shouldReceiveTouch:, grab the given gestureRecognizer, make sure it's your UIPanGestureRecognizer
and use its translationInView:
method to find out if the translation is on the X or Y axis. Use that information to decide whether you want to return YES or NO. For example:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if([gestureRecognizer isEqual:myTapGesture]) {
CGPoint point = [gestureRecognizer translationInView:self.collectionView];
if(point.x != 0) { //adjust this condition if you want some leniency on the X axis
//The translation was on the X axis, i.e. right/left,
//so this gesture recognizer shouldn't do anything about it
return NO;
}
}
return YES;
}
Solution 2
Having the -shouldReceiveTouch: and -shouldRecognizeSimultaneouslyWithGestureRecognizer: returning YES, add UIGestureRecognizerDelegate as your class protocol list and delegate your gestures delegate to self in -viewDidLoad.
yourGesture.delegate = self;
Erika Electra
I am a female programmer. I like Cinderella and Disney.
Updated on June 05, 2022Comments
-
Erika Electra almost 2 years
I have a
UICollectionView
implementing a grid-based layout of customUICollectionViewCells
. To allow cells to respond to dragging, I individually add aUIPanGestureRecognizer
to each cell.The
UICollectionView
still scrolls (horizontally) when I touch down and swipe left/right starting at points between cells, but as long as the pan gesture recognizer is added to a cell, it seems like theCollectionView
refuses to scroll when I start my swipe tapping within a cell.Right now, I separate horizontal left/right drags from vertical up/down drags, so there should not be any conflict between dragging cells out (vertical swipes) and scrolling the
CollectionView
(Horizontal swipes). In this case, how can I pass the swipe to the collection/scroll view so it knows to scroll like normal? It's really annoying to have to start on the boundary or spacing between cells.Once I remove the pan gesture from a cell, scrolling works as normal no matter if I start swiping on a cell or between cells.
EDIT:Desired pan gesture behavior posted below as current code
// Handle pans by detecting swipes: -(void) handlePan:(UIPanGestureRecognizer*)recognizer { // Calculate touch location CGPoint touchXY = [recognizer locationInView:masterWindowView]; // Handle touch if (recognizer.state == UIGestureRecognizerStateBegan) { gestureWasHandled = NO; pointCount = 1; startPoint = touchXY; } if (recognizer.state == UIGestureRecognizerStateChanged) { ++pointCount; // Calculate whether a swipe has occurred float dX = deltaX(touchXY, startPoint); float dY = deltaY(touchXY, startPoint); BOOL finished = YES; if ((dX > kSwipeDragMin) && (ABS(dY) < kDragLimitMax)) { touchType = TouchSwipeLeft; NSLog(@"LEFT swipe detected"); [recognizer requireGestureRecognizerToFail:recognizer]; //[masterScrollView handlePan] } else if ((dX < -kSwipeDragMin) && (ABS(dY) < kDragLimitMax)) { touchType = TouchSwipeRight; NSLog(@"RIGHT swipe detected"); [recognizer requireGestureRecognizerToFail:recognizer]; } else if ((dY > kSwipeDragMin) && (ABS(dX) < kDragLimitMax)) { touchType = TouchSwipeUp; NSLog(@"UP swipe detected"); } else if ((dY < -kSwipeDragMin) && (ABS(dX) < kDragLimitMax)) { touchType = TouchSwipeDown; NSLog(@"DOWN swipe detected"); } else finished = NO; // If unhandled and downward, produce a new draggable view if (!gestureWasHandled && finished && (touchType == TouchSwipeDown)) { [self.delegate cellBeingDragged:self]; dragView.center = touchXY; dragView.hidden = NO; dragView.backgroundColor = [UIColor clearColor]; masterScrollView.scrollEnabled = NO; // prevent user from scrolling during gestureWasHandled = YES; } else if (gestureWasHandled) { // allow continued dragging after detection dragView.center = touchXY; } } if (recognizer.state == UIGestureRecognizerStateEnded) { // ensure that scroll view returns to scrollable if (gestureWasHandled) { [self.delegate cell:self dragEndedAt:touchXY]; } } } // Allow simultaneous recognition -(BOOL) gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer*)otherGestureRecognizer { return YES; }
This code works when given to each individual cell. It does NOT work when attached to the UICollectionView as its gesture recognizer, and it in fact stops all scrolling.
-
Erika Electra almost 10 yearsI tried this, and the UICollectionView stopped scrolling entirely, in addition to not detecting my swipes on each individual cell. I went back to my original approach, which at least lets me scroll by touching between icons!
-
KerrM almost 10 yearsShow some code on what you have tried and how you implemented the solution I proposed. I should add that you should have implemented the
UIGestureRecognizerDelegate
protocol (specifically thegestureRecognizer:shouldReceiveTouch:
) to decide whether a cell should receive the gesture or not. -
Erika Electra almost 10 yearsActually, yes, it would help very much to know the delegate functions that I have to overwrite -- and whether to overwrite them on the ViewController, the UICollectionView, the UICollectionViewFlowLayout, or the UICollectionViewCell. I've already overwritten handleGestureSimultaneously to always return YES. Will post code here in a moment
-
Erika Electra almost 10 yearsCode posted! Thanks!
-
KerrM almost 10 yearsAll your gesture recognizer methods should be in your view controller, or perhaps in a custom class, but not in your views or flow layout subclass.
-
Erika Electra almost 10 yearsIs there any delegate method that I need to implement and explicitly tell the UICollectionView (in your solution) to treat the left & right swipes as normal scrolls, such as calling another recognizer, while keeping my current code for down swipes on cells?
-
KerrM almost 10 yearsUmm, I'm not sure. I would refactor the code to handle only one gesture recognizer in the collection view. I've updated my answer with an example.
-
amit09gupta almost 9 years@Cindeselia is the answer loosing on being accepted?