collectionView didSlectRow bug

2019-09-25 07:14发布

问题:

Hello on a specific viewController of my project i have a UICollectionView with a custom class cell. But i have a big problem this that func :

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        print("tapped on a cell")
    }

when is click on a cell and instant realease (normal click) it does nothing, just nothing.

if i press and hold about 1s without release the finger it become grey, highlighted

And if i press and hold at least 3 seconds release the finger didSelectItemAt is executed correctly. I tried to do the same on another project and that's work great but not on this VC and i really don't find the problem. The VC Bugged is of addTest clas in Main.storyboard

回答1:

The insight of Mojtaba Hosseini is very clever, but the answer given might not be quite correct.

It turns out that there is a UITapGestureRecognizer on the main view; if it recognizes before the tap on the cell, it prevents cell selection. But if you merely set cancelsTouchesInView to false on that gesture recognizer, then they both operate, and that seems unlikely to be what is wanted. We surely want the cell tap and not the tap gesture recognizer tap.

The correct solution is thus to give the tap gesture recognizer a delegate and implement gestureRecognizerShouldBegin. Here, we look to see where the tap is. If it is within the bounds of a cell, we return false; otherwise we return true. We thus mediate between the cell tap and gesture recognizer tap.

Here is a possible implementation, demonstrated in a highly simplified form:

extension UIView {
    func isDescendant(of whattype:UIView.Type) -> Bool {
        var sup : UIView? = self.superview
        while sup != nil {
            if (whattype == type(of:sup!)) {
                return true
            }
            sup = sup!.superview
        }
        return false
    }
}

class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UIGestureRecognizerDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
        let t = UITapGestureRecognizer(target: self, action: #selector(tap))
        self.view.addGestureRecognizer(t)
        t.delegate = self
    }

    @objc func tap(_:UIGestureRecognizer) {
        print("tap")
    }

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 1
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
        return cell
    }

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

    func gestureRecognizerShouldBegin(_ gr: UIGestureRecognizer) -> Bool {
        if let v = gr.view {
            let loc = gr.location(in: v)
            if let v2 = v.hitTest(loc, with: nil) {
                return !v2.isDescendant(of: UICollectionViewCell.self)
            }
        }
        return true
    }
}

As you can see, we look to see whether the tap is inside a collection view cell; if it is, our gesture recognizer is prevented from recognizing, and the selection succeeds immediately.



回答2:

Probably there is a UIGesture or another interactable thing underneath the collection view. You should DISABLE its ability to cancel touches in view in interface builder:

or in code:

myTapGestureRecognizer.cancelsTouchesInView = false