0

I can't figure this out.

I have big UIView with UITapGestureRecognizer.

On this view I put (smaller) UITableView. And now, if I tap on cell, didSelectRowAt is not called, because UIView tap recognizer detect the tap. (It's method is called.)

How do I solve this, that UITableView don't pass touch through to the view.

I try with setting

table.isUserInteractionEnabled = true

and

table.isExclusiveTouch = true

but it doesn't help

EDIT :

A lot of your answers suggest that I have a tableView below view, which is not the case.

I will paste some of my code (changed a little bit for convenience):

class Panel: UIView {

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setTapGesture()
    }

    func setTapGesture() {

        tapGesture = UITapGestureRecognizer.init(target: self, action: #selector(UIControlCenterPanel.tapOnPanel(recogniser:)))
        self.addGestureRecognizer(tapGesture)
    }

    func tapOnPanel(recogniser : UITapGestureRecognizer) {
        print("Tap was made")
    }
 }


  class MyPanel: Panel, UITableViewDelegate {
      let table = MyTable()

      override init(frame: CGRect) {
          super.init(frame: frame)

          table.table.delegate = self
          table.isUserInteractionEnabled = true  // This was added as a test
          table.table.isExclusiveTouch = true    // This was added as a test
          table.translatesAutoresizingMaskIntoConstraints = false
          addSubview(table)
          .... // constraints added below
      }

      func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
          print("cell tapped")
      }
  }

  class MyTable: UIView {

      let table = UITableView()

      override init(frame: CGRect) {
          super.init(frame: frame)

          table.translatesAutoresizingMaskIntoConstraints = false
          addSubview(table)
      }
  }

If I click on the cell now, it print:

Tap was made

If I comment out the gesture recogniser like this :

func setTapGesture() {

       // tapGesture = UITapGestureRecognizer.init(target: self, action: #selector(UIControlCenterPanel.tapOnPanel(recogniser:)))
       // self.addGestureRecognizer(tapGesture)
}

then it is printed :

cell tapped
Rishil Patel
  • 1,977
  • 3
  • 14
  • 30
Marko Zadravec
  • 8,298
  • 10
  • 55
  • 97

5 Answers5

1

You can override your container UIView's hitTest method to only let subviews (if any) receive touch events, but never itself. This allows search to continue to unrelated views below. Just replace your UIView with an instance of this class:

class PassThroughView: UIView {

    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        let result = super.hitTest(point, with: event)
        return result == self ? nil : result
    }
}
Clafou
  • 15,250
  • 7
  • 58
  • 89
  • If your container UIView is created in Interface Builder you can do so using the Identity Inspector: select it and change its class from UIView to PassThroughView. – Clafou Mar 03 '17 at 13:25
  • Hi, thank you for your answer. I actually thought about that, but in my mind it make no sense that UIView that is a parent of a UITableView, (which is subView) receive a touch instead of tableView. Please look at my edit... Can you explain why this happened? – Marko Zadravec Mar 03 '17 at 20:07
  • Oh wait, that's wrong. My answer is for cases where your UIView is above and is not a parent. Sorry. – Clafou Mar 04 '17 at 08:09
  • Can you help me with this one. I even created small app that has only this classes and it doesn't work – Marko Zadravec Mar 04 '17 at 08:33
0

Returning appropriate BOOL values in gestureRecognizer:shouldReceiveTouch: will enable you to propagate touches to desired subviews.
Some sample code to achieve this can be like:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{

    if([touch.view isDescendantOfView:table]){
        return NO; // or YES, as desired
    }
}
  • This function needs to be implemented in the ViewController.m
  • You'll need an instance of table, either as an @propertyor extracted via viewWithTag:
ystack
  • 1,785
  • 12
  • 23
0

let tap = UITapGestureRecognizer(target: self, action: Selector("handleTap:")) tap.delegate = self tap.cancelsTouchesInView = false view.addGestureRecognizer(tap)// default view

func handleTap(sender: UITapGestureRecognizer? = nil) { view.endEditing(true) // keyboars hides }

Vignesh J
  • 257
  • 1
  • 2
  • 14
  • Sorry but I don't quite understand which tap should I set false for cancelsTouchesInView? TableView (because it is a sub class of the UIScrollView) has only pan and pinch gesture recogniser... – Marko Zadravec Mar 03 '17 at 20:04
  • let tap = UITapGestureRecognizer(target: self, action: Selector("handleTap:")) tap.delegate = self tap.cancelsTouchesInView = false view.addGestureRecognizer(tap) // default view func handleTap(sender: UITapGestureRecognizer? = nil) { view.endEditing(true) // keyboars hides } – Vignesh J Mar 04 '17 at 05:03
  • view is default view. this method used to restrict the TapGestureRecognizer when you select the didSelectRowAt cell – Vignesh J Mar 04 '17 at 05:16
0

Try this code

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
    if touch.view.isDescendantOf(yourTableView) {
        return false
    }
    return true
}
Vignesh J
  • 257
  • 1
  • 2
  • 14
0

You can refer to the answers stated in the question.

Link: UITapGestureRecognizer breaks UITableView didSelectRowAtIndexPath

Setting delegate to filter valid touches according to the type of touched view is the best solution. Some answers suggest to use cancelsTouchesInView = false to tackle this problem but it actually fire both the UITableView didSelectRowAt and also the UITapGestureRecognizer selector at the same time. Choose the right one for yourself.

Myrick Chow
  • 332
  • 1
  • 6
  • 16