Aaron
Aaron

Reputation: 6714

How do I trigger a collection view's didSelectItemAtIndexPath: on tap while it's scrolling?

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

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

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

Upvotes: 1

Views: 1335

Answers (2)

NomNom
NomNom

Reputation: 73

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)
        }
}

Upvotes: 0

JohnMorrison
JohnMorrison

Reputation: 504

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

}

}

Upvotes: 2

Related Questions