Search code examples
iosswiftuiviewuitapgesturerecognizer

Tap gesture events on overlapping area


I have a view that is with the black border and it has two different views on it. And these views are overlapping in a small area. And each of them has own UITapGestureRecognizer. When I tap each item's discrete area, the action of that item is triggered. But when I tap the common area, only the second view's action is triggered. I want that both actions have to be triggered. How can I achieve this? Here is my code:

class ViewController: UIViewController {

  @IBOutlet weak var view1: UIView!
  @IBOutlet weak var view2: UIView!
  @IBOutlet weak var outerView: UIView!

  override func viewDidLoad() {
      super.viewDidLoad()
      outerView.layer.borderWidth = 2.0
      outerView.layer.borderColor = UIColor.black.cgColor
      view1.layer.borderWidth = 2.0
      view1.layer.borderColor = UIColor.red.cgColor
      view2.layer.borderWidth = 2.0
      view2.layer.borderColor = UIColor.blue.cgColor
      self.initialize()
  }

  private func initialize(){
      let tapGesture1 = UITapGestureRecognizer(target: self, action: #selector(detectTap1(_:)))
      let tapGesture2 = UITapGestureRecognizer(target: self, action: #selector(detectTap2(_:)))
      self.view1.addGestureRecognizer(tapGesture1)
      self.view2.addGestureRecognizer(tapGesture2)
  }

  @objc func detectTap1(_ gesture : UITapGestureRecognizer) {
      print("detectTap1")
  }

  @objc func detectTap2(_ gesture : UITapGestureRecognizer) {
      print("detectTap2")
  }
}

example

Kindly share your suggestions.


Solution

  • For this problem i have found this solution, maybe is not the best solution but it works, i will look for further improvements anyway

    I had subclassed UIGestureRecognizer class

    Updated

    import UIKit
    import UIKit.UIGestureRecognizerSubclass
    
    class CustomGestureRecognizer: UIGestureRecognizer {
    
        var anotherGestureRecognizer : CustomGestureRecognizer?
        private var touchBeganSended : Bool = false
        private var touchLocation : CGPoint?
    
        override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
            super.touchesBegan(touches, with: event)
            if let validTouch = touches.first?.location(in: self.view) {
                if (self.view!.point(inside: validTouch, with: event)) {
                    if(!touchBeganSended) {
                        touchBeganSended = true
                        touchLocation = validTouch
                        anotherGestureRecognizer?.touchesBegan(touches, with: event)
                        state = .recognized
                    }
                }
            }
        }
    
        override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
            super.touchesEnded(touches, with: event)
            if let validTouch = touches.first?.location(in: self.view) {
                if (self.view!.point(inside: validTouch, with: event)) {
                    if(touchBeganSended) {
                        touchBeganSended = false
                        anotherGestureRecognizer?.touchesEnded(touches, with: event)
                        state = .recognized
                    }
                }
            }
        }
    
        override func location(in view: UIView?) -> CGPoint {
            if let desiredView = view {
                if(desiredView == self.view) {
                    return touchLocation ?? CGPoint(x: 0, y: 0)
                } else {
                    return super.location(in: view)
                }
            } else {
                return super.location(in: view)
            }
        }
    }
    

    Updated

    then you need to modify your initialize() method to this one, with the last update you don't need to take into account which view is on top on view hierarchy

    private func initialize(){
        let tapGesture1 = CustomGestureRecognizer(target: self, action: #selector(detectTap1(_:)))
        let tapGesture2 = CustomGestureRecognizer(target: self, action: #selector(detectTap2(_:)))
        tapGesture1.cancelsTouchesInView = true
        tapGesture1.delegate = self
        tapGesture2.cancelsTouchesInView = true
        tapGesture2.delegate = self
        self.view1.addGestureRecognizer(tapGesture1)
        tapGesture1.anotherGestureRecognizer = tapGesture2
        tapGesture2.anotherGestureRecognizer = tapGesture1
        self.view2.addGestureRecognizer(tapGesture2)
    }
    

    this works as you can see here

    enter image description here