1

The default behavior while a collection view is mid-scroll:

  • tap #1: stops the scrolling
  • tap #2: triggers didSelectItemAtIndexPath

What I want while a collection view is mid-scroll:

  • tap #1: triggers didSelectItemAtIndexPath

What would be a clean, correct approach to achieve this? FWIW, I realize this might be unexpected behavior.

Aaron
  • 6,466
  • 7
  • 37
  • 75

2 Answers2

2

I think the best approach is to use the UICollectionView addGestureRecognizer to add a touch gesture recognizer, then process the touch gesture (e.g. get the touch location in the collection view, use that to get the indexPath of the item that was touched, then call the collectionView.didSelectItemAtIndexPath yourself). As for the scrolling, you could use the UISrollViewDelegate methods to disable user interaction on the collection view once the scroll starts, then re-enable user interaction on the collection view once the scrolling stops and/or in the viewDidDisappear view controller function.

Like this:

public class MyViewController: UIViewController {

@IBOutlet weak var collectionView: UICollectionView!

var collectionViewTap:UITapGestureRecognizer?

override public func viewDidLoad() {

collectionViewTap = UITapGestureRecognizer(target: self, action: #selector(handleTap))
self.view.addGestureRecognizer(collectionViewTap!)

}

override public func viewDidDisappear(animated: Bool) {

  collectionView.userInteractionEnabled = true

}

func handleTap (sender:UITapGestureRecognizer) {

let touchPoint = sender.locationOfTouch(0, inView: collectionView)

let indexPath = collectionView.indexPathForItemAtPoint(touchPoint)

if (indexPath != nil) {
    collectionView(collectionView, didSelectItemAtIndexPath: indexPath!)
}

}

public func scrollViewWillBeginDragging(scrollView: UIScrollView) {

   collectionView.userInteractionEnabled = false

}

public func scrollViewDidEndDecelerating(scrollView: UIScrollView) {

   collectionView.userInteractionEnabled = true

}

}

JohnMorrison
  • 504
  • 3
  • 4
0

In your initializer for the collection view, add an additional target for the pan gesture self.panGestureRecognizer.addTarget(self, action: #selector(allowSelectionOfItemDuringScroll(_:)))

Then, you can implement it like this:

@objc private func allowSelectionOfItemDuringScroll(_ sender: UIPanGestureRecognizer) {
        let yTranslation = sender.translation(in: self).y
        
        var isScrolling: Bool {
            if sender.state == .began {
                return false
            }
            if isDragging && isDecelerating {
                return false
            }
            return isDragging || isDecelerating
        }
        
        if yTranslation == 0 && isScrolling {
            let selectionPoint = sender.translation(in: self)
            if let index = indexPathForItem(at: selectionPoint) {
                self.delegate?.collectionView?(self, didSelectItemAt: index)
        }
}
NomNom
  • 73
  • 1
  • 7