26

Task

Add a single tap gesture to UICollectionView, do not get in the way of cell selection.

I want some other taps on the no-cell part of the collectionView.

Code

Using XCode8, Swift 3.

override func viewDidLoad() {
    ...
    collectionView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(tap)))
}

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    print(indexPath)
}

func tap(sender: UITapGestureRecognizer){
    print("tapped")
}

Result

Yeah, it gets in the way now. When you tap on cell, it logs "tapped".

Analysis

  • I check the hitTest return value of the collectionView and the cell. Both returned the tapped cell, which means they form a responder chain of Cell -> CollectionView
  • no gestures on the cell
  • 3 gestures on collectionView, no one seems to work with the cell select
    • UIScrollViewDelayedTouchesBeganGestureRecognizer
    • UIScrollViewPanGestureRecognizer
    • UITapGestureRecognizer
  • callStack, seems cell selection has a different stack trace with gesture's target-action pattern.
  • double tap gesture works along with cell selection.

Question

Couldn't find more trace. Any ideas on how cell selection is implemented or to achieve this task?

jscs
  • 63,694
  • 13
  • 151
  • 195
addlistener
  • 871
  • 1
  • 12
  • 20

3 Answers3

44

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.

Yi Jiang
  • 49,435
  • 16
  • 136
  • 136
Josh Homann
  • 15,933
  • 3
  • 30
  • 33
17

Instead of trying to force didSelectItem you can just get the indexPath and/or cell this way:

func tap(sender: UITapGestureRecognizer){

    if let indexPath = self.collectionView?.indexPathForItem(at: sender.location(in: self.collectionView)) {
        let cell = self.collectionView?.cellForItem(at: indexPath)
        print("you can do something with the cell or index path here")
    } else {
        print("collection view was tapped")
    }
}
Frankie
  • 11,508
  • 5
  • 53
  • 60
  • Thanks! This is really helpful. I'm going to add more gestures to the collectionView. Seems like UICollectionView is analysing raw touches rather than using a high level gesture recogniser, and that's limited. – addlistener Nov 03 '16 at 08:25
0

I get another way: when adding gestures, set delegate, implement below method

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch

you can decide whether the gesture recognizer to receive the touch by your own logic, with position or the page's hidden property, based on your demand.

childrenOurFuture
  • 1,889
  • 2
  • 14
  • 23