swift isUserInteractionEnabled = false not working inside UITapGestureRecognizer

1.2k views Asked by At

I have a problem where I have a UIScrollView and a Header (UIView) inside my main View and my Header is over my UIScrollView as such:

UIView.
|
|- UIScrollView.
|
|- Header. (UIView)

I want my header to be able to detect taps on it, but I also want my scroll view to be able to scroll when I drag over my Header which right now it is not possible because my Header is over it and is blocking the scroll.

To sum up, I want my Header to detect taps but forward scrolls to my UIScrollView.

To tackle this problem I tried multiple things, and here are some of them:

  1. Adding a UIPanGestureRecognizer to my Header so it is able to detect dragging
  2. Adding a UITapGestureRecognizer to my Header so it is able to detect tapping
  3. Setting isUserInteractionEnabled = false when dragging begins so the gesture can be passed to the next UIResponder which in this case is my UIScrollView
  4. Setting isUserInteractionEnabled = true once my dragging has finished so it can again detect tapping

This is the code snippet:

    override func viewLoad() {
        myScreenEdgePanGestureRecognizer = UIPanGestureRecognizer(target: self, action:#selector(handlePan))
        let tapGestureRecognizer = UITapGestureRecognizer(target: self, action:#selector(handleTap(_:)))
        headerView.addGestureRecognizer(myScreenEdgePanGestureRecognizer)
        headerView.addGestureRecognizer(tapGestureRecognizer)
    }

    @objc func handlePan(_ sender: UITapGestureRecognizer){
        print("dragging")
        if headerView.isUserInteractionEnabled{
            headerView.isUserInteractionEnabled = false
        }
        if sender.state == .began {
        } else if sender.state == .ended {
            headerView.isUserInteractionEnabled = true
        }
    }

    @objc func handleTap(_ sender: UITapGestureRecognizer){
        print("tapped")
    }

At this point I see how dragging and tapping are being detected just fine, but for some reason isUserInteractionEnabled = false seems to not be changing how the view is behaving. This code is acting as isUserInteractionEnabled is always true no mater what.

Things that I have also tried besides this:

  • overriding the hitTest function inside UIButton
  • overriding touchesBegan, touchesMoved, touchesEnded methods overriding next
  • setting the variable to return ScrollView as the next UIResponder
  • setting the isExclusiveTouch method in UIButton
  • changing the isUserInteractionEnabled in every way possible
1

There are 1 answers

3
Ondřej Korol On

I was struggling with this problem too, you should try to use methods of UIGestureRecognizerDelegate which allows you to handle simultaneous gestures.

  1. Connect your gesture recognizers delegates e.g. tapGestureRecognizer.delegate = self
  2. Make your ViewController conform this protocol e.g. extension YourViewController: UIGestureRecognizerDelegate {}
  3. Implement this function:

    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { return true }