4

I have a UICollectionViewController class that lays out bunch of cells. When the user touches a cell, I want to use the touchesBegan method to prevent new touches until the first touch has completed its selection of a cell.

I tried putting the following code in my view controller but it never gets called.

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    super.touchesBegan(touches, withEvent: event)
    print("touches began")
}

However, elsewhere in my project I have another class that's a subclass of UIViewController. There, I use touchesBegan in the following code to dismiss the keyboard for a text view in that class, and that code gets called just fine.

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    super.touchesBegan(touches, withEvent: event)
    view.endEditing(true)
}

Why does this code work in one class and not the other? And how can I use touchesBegan in my UICollectionViewController to detect touches on my collection view cells? Solutions in Swift would be appreciated. Thanks for your time!

Research: I checked out this solution: Handle Touch in UiCollectionView? But the answers mostly apply to that specific project.

I tried using a gesture recognizer to run self.collectionView?.userInteractionEnabled = false but the gesture recognizer does not fire for selector.state == .Began, only for .Ended. So I could not use it to prevent further touches.

I also tried using a UILongPressGestureRecognizer to do the same thing, but that gesture prevents the default tap gesture which the collection view cells listen for, so the regular tap on the cells was never received and thus the cells cannot be selected.

I posted a workaround as an answer, although it does not answer the original question.

peacetype
  • 1,928
  • 3
  • 29
  • 49
  • 1
    I suspect one of your cells may not be forwarding the touch up the responder chain. If you have custom collection view cells, make sure they are calling `super.touchesBegan...` – Andrew Nov 08 '15 at 01:57
  • 1
    I don't know why it's not calling (I think Santa Claus is on the right path), but could you instead have a `var hasBeenTouched : Bool` variable, that is set to `true` after the first run of code? Then the code to run is all placed in an `if hasBeenTouched == false` block. – Tim Nov 08 '15 at 05:21
  • 1
    @Tim - I did try that idea. In `collectionView(:didSelectItemAtIndexPath)`, if `hasBeenTouched == false` I set it to `true`, disable user interaction, and run a block to handle cell selection. At the end of the block I set `hasBeenTouched` to back to false and enable user interaction. But for some reason it's still possible for 2 cells to become selected at the same time which causes the cell selection functionality to "lock up". I don't really understand why that approach does not work. – peacetype Nov 08 '15 at 19:29
  • 1
    @SantaClaus - I do have custom collection view cells. To make a call to `super.touchesBegan` for the cells, would I make that call within the `collectionView:cellForItemAtIndexPath:` method of my `UICollectionViewController` subclass? – peacetype Nov 08 '15 at 19:38
  • 2
    Did you read the documentation [here?][1] [1]: https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIResponder_Class/#//apple_ref/occ/instm/UIResponder/touchesBegan:withEvent: You have to override all methods or none. Maybe that's the difference in your two controllers. – Tim Nov 09 '15 at 02:30
  • 1
    @Tim - It says, "*If you override this method without calling super, you must also override the other methods for handling touch events, if only as stub (empty) implementations.*" However, I do call super in my code above, in both controllers. I also tried overriding all 5 methods (and called super in them) and still none of them were called. Must be something else. I read some related questions which seemed to imply that there needs to be some sort of message passing along the receiver chain for this to work. So maybe I should look into that more. – peacetype Nov 11 '15 at 01:08

1 Answers1

0

This is sort of a workaround but instead of using touchesBegan I used the UICollectionViewDelegate protocol and this works for letting me select a cell in UICollectionView.

// The specified item should be selected.
override func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
    return true
}

override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    colorIndex = (indexPath as NSIndexPath).item // sets color index to the selected cell's path
    let selectedCell = collectionView.cellForItem(at: indexPath) as UICollectionViewCell!
print("Cell selected: \(selectedCell)")
peacetype
  • 1,928
  • 3
  • 29
  • 49