Nikita Vlasenko
Nikita Vlasenko

Reputation: 4352

Selecting UICollectionViewCell in the presence of UITapGestureRecognizer

I am trying to respond to UICollectionViewCell selection:

private func setupCellAction() {
    collectionView?.rx.itemSelected
        .asObservable()
        .subscribe(onNext: { [weak self] indexPath in
            print("itemSelected!")
            let cell = self?.collectionView?.cellForItem(at: indexPath) as? CellTypeCollectionViewCell
            self?.performSegue(withIdentifier: "showBarchartSegue", sender: cell)
        }).disposed(by: disposeBag)
}

But somehow onNext method is never called. I tried putting setupCellAction() in viewDidLoad, viewWillAppear and viewDidAppear but it is not working. Any suggestions would be greatly appreciated.

Update

I tried the suggestion from the following thread: How to select CollectionView cell in RxSwift

and added .debug("RX: Model selected") before the subscribe method. I see the output in the console that it is subscribed once.

Update

I tried rewriting the setupCellAction() in the following way:

private func setupCellAction() {
    collectionView?.rx.modelSelected(CellTypeCollectionViewCell.self)
        .asObservable()
        .debug("RX: Model selected")
        .subscribe(onNext: { [weak self] cell in
            print("itemSelected!")
            self?.performSegue(withIdentifier: "showBarchartSegue", sender: cell)
        }).disposed(by: disposeBag)
}

It is not working either. I see also that it is subscribed once in the console.

Update

UICollectionViewController was embedded in another container UIViewController, and in it I defined UITapGestureRecognizer. After commenting out the code for the UITapGestureRecognizer, the itemSelected() method started to work. Right now I need a way to let the tap event through if it happened on the UICollectionViewCell. Is there a way to do that?

The code for tapping in the container controller (viewDidLoad):

let tap = UITapGestureRecognizer(target: self, action: 
#selector(self.handleTap(_:)))
    tap.delegate = self
    view.addGestureRecognizer(tap)

The handleTap():

@objc func handleTap(_ sender: UITapGestureRecognizer) {
    tableView.isHidden = true
    searchBar.resignFirstResponder()
}

Upvotes: 0

Views: 1259

Answers (1)

Matheus Boeira
Matheus Boeira

Reputation: 66

You can let taps through with the UIGestureRecognizerDelegate protocol and implementing the method func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool.

Basically, you need to return false whenever you touch a UICollectionViewCell, if I understood the problem correctly.

You can do this by using the method func indexPathForItem(at point: CGPoint) -> IndexPath? from the UICollectionView. If the given CGPoint matches a cell's location, you will get its IndexPath.

Don't forget to translate the touch location to the collection view's frame - you can use UITouch's func location(in view: UIView?) -> CGPoint for this.

It would probably look somewhat like this:

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    let point = touch.location(in: collectionView)
    return collectionView.indexPathForItem(at: point) == nil
}

Upvotes: 5

Related Questions