7

Using UICollectionView, is it possible to select multiple cells by dragging your finger over a few of them? E.g., if you drag your finger over a row of 6, and down into the next row, it would select all of them.

Tried something simple:

UISwipeGestureRecognizer *swipeGuesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwipeGesture:)];
[self.collectionView addGestureRecognizer:swipeGuesture];

But that seemed to only call the method on the first cell that was touched.

Any ideas?

Nic Hubbard
  • 41,587
  • 63
  • 251
  • 412
  • 2
    Have you tried using a UIPanGestureRecognizer? And based on the location of the pan events, tracking what cells are passed through. When the gesture ends, you would have an array of selected cells. – Mike Welsh Dec 09 '14 at 23:36
  • @MikeWelsh solution is an option, also you can try override UICollectionView class and override touches functions like: `- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event` `- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event` and others – Vitalii Gozhenko Dec 09 '14 at 23:41
  • @MikeWelsh Brilliant! That worked! Can you add that as an answer? – Nic Hubbard Dec 09 '14 at 23:45
  • @MikeWelsh - The issue with that solution though, is that it blocks scrolling of the entire `UICollectionView`. – Nic Hubbard Dec 10 '14 at 00:22
  • @NicHubbard You could check that the initial pan is horizontal, and ignore it if it's not. You'd have to configure the recognizer and delegate, but it's certainly do-able. – Mike Welsh Dec 10 '14 at 01:00
  • @NicHubbard any custom library available for this feature? – Dixit Akabari May 24 '19 at 12:01

4 Answers4

10

Swift 3: Swipe to select with auto scrolling and working scroll.

var selectMode = false
var lastSelectedCell = IndexPath()

func setupCollectionView() {
    collectionView.canCancelContentTouches = false
    collectionView.allowsMultipleSelection = true
    let longpressGesture = UILongPressGestureRecognizer(target: self, action: #selector(didLongpress))
    longpressGesture.minimumPressDuration = 0.15
    longpressGesture.delaysTouchesBegan = true
    longpressGesture.delegate = self
    collectionView.addGestureRecognizer(longpressGesture)

    let panGesture = UIPanGestureRecognizer(target: self, action: #selector(didPan(toSelectCells:)))
    panGesture.delegate = self
    collectionView.addGestureRecognizer(panGesture)
}

func selectCell(_ indexPath: IndexPath, selected: Bool) {
    if let cell = collectionView.cellForItem(at: indexPath) {
        if cell.isSelected {
            collectionView.deselectItem(at: indexPath, animated: true)
            collectionView.scrollToItem(at: indexPath, at: UICollectionViewScrollPosition.centeredVertically, animated: true)
        } else {
            collectionView.selectItem(at: indexPath, animated: true, scrollPosition: UICollectionViewScrollPosition.centeredVertically)
        }
        if let numberOfSelections = collectionView.indexPathsForSelectedItems?.count {
            title = "\(numberOfSelections) items selected"
        }
    }
}

func didPan(toSelectCells panGesture: UIPanGestureRecognizer) {
    if !selectMode {
        collectionView?.isScrollEnabled = true
        return
    } else {
        if panGesture.state == .began {
            collectionView?.isUserInteractionEnabled = false
            collectionView?.isScrollEnabled = false
        }
        else if panGesture.state == .changed {
            let location: CGPoint = panGesture.location(in: collectionView)
            if let indexPath: IndexPath = collectionView?.indexPathForItem(at: location) {
                if indexPath != lastSelectedCell {
                    self.selectCell(indexPath, selected: true)
                    lastSelectedCell = indexPath
                }
            }
        } else if panGesture.state == .ended {
            collectionView?.isScrollEnabled = true
            collectionView?.isUserInteractionEnabled = true
            swipeSelect = false
        }
    }
}

func didLongpress() {
    swipeSelect = true
}
EmiliaKron
  • 156
  • 1
  • 5
  • 1
    this worked very well, One thing I have to change is to select cell in `panGesture.state == .ended` state for smooth animation – M Zubair Shamshad Apr 04 '19 at 13:24
  • The provided example does not work in all cases. For one thing, you need to implement gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:. In addition, you need to check the pan direction (horizontal or vertical) first. If it's not horizontal primarily, then fall back to scrolling. – Nik Bhatt Nov 06 '19 at 00:07
3

You could use UIPanGestureRecognizer. And based on the location of the pan events, tracking what cells are passed through. When the gesture ends, you would have an array of selected cells.

Make sure that cancelsTouchesInView is set to NO. You'll need to set the delegate with gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer: and gestureRecognizerShouldBegin implemented to make sure the CollectionView can still scroll

Mike Welsh
  • 808
  • 5
  • 10
2

In iOS 13 and later, you can simply use the built-in multiselection gestures. See Selecting Multiple Items with a Two-Finger Pan Gesture. Make sure you set collectionView.allowsMultipleSelectionDuringEditing to true.

Hristo
  • 6,382
  • 4
  • 24
  • 38
0

to enable drag select, from iOS 14 onwards we can set 2 collection view properties.

collectionView.isEdit = true
collectionView.allowsMultipleSelectionDuringEdit = true

these will call didSelect/didDeselect method, and we can configure cell selection