skip to Main Content

When the UITapGestureRecognizer is not added to the view, both the UIButton and CustomControl will receive the .touchUpInside event. However, when the UITapGestureRecognizer is added to the view, only the UIButton can receive the .touchUpInside event. How can a custom UIControl be configured to respond to the touch event like UIButton in this scenario?

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
    
        let uiButton = UIButton(frame: CGRect(x: 100, y: 100, width: 100, height: 100))
        uiButton.backgroundColor = .red
        uiButton.addTarget(self, action: #selector(onUIButtonClick), for: .touchUpInside)
        view.addSubview(uiButton)
        
        let customButton = CustomControl(frame: CGRect(x: 200, y: 100, width: 100, height: 100))
        customButton.backgroundColor = .blue
        customButton.addTarget(self, action: #selector(onCustomControlClick), for: .touchUpInside)
        view.addSubview(customButton)
        
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(onTap))
        view.addGestureRecognizer(tapGesture)
    }
    
    @objc func onTap() {
        debugPrint("onTap")
    }
    
    @objc func onUIButtonClick() {
        debugPrint("onUIButtonClick")
    }
    
    @objc func onCustomControlClick() {
        debugPrint("onCustomControlClick")
    }
}

class CustomControl: UIControl {
}

2

Answers


  1. class ViewController: UIViewController {
        
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(onTap))
        
        override func viewDidLoad() {
            super.viewDidLoad()
        
            let uiButton = UIButton(frame: CGRect(x: 100, y: 100, width: 100, height: 100))
            uiButton.backgroundColor = .red
            uiButton.addTarget(self, action: #selector(onUIButtonClick), for: .touchUpInside)
            view.addSubview(uiButton)
            
            let customButton = CustomControl(frame: CGRect(x: 200, y: 100, width: 100, height: 100))
            customButton.backgroundColor = .blue
            customButton.addTarget(self, action: #selector(onCustomControlClick), for: .touchUpInside)
            view.addSubview(customButton)
            
            tapGesture.cancelsTouchesInView = false
            view.addGestureRecognizer(tapGesture)
        }
        
        @objc func onTap() {
            debugPrint("onTap")
        }
        
        @objc func onUIButtonClick() {
            debugPrint("onUIButtonClick")
        }
        
        @objc func onCustomControlClick() {
            debugPrint("onCustomControlClick")
        }
    }
    

    The tap gesture will not cancel touches for other views in its view’s hierarchy if cancelsTouchesInView is set to false. This manner, the tap gesture will not interfere with the touch events of the custom control.

    Login or Signup to reply.
  2. The delegate method gestureRecognizerShouldBegin(_:) asks if the gesture should read (and interrupt) touches. In your case:

    class CustomControl: UIControl {
        override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
            return !(gestureRecognizer is UITapGestureRecognizer)
        }
    }
    

    You can expand the conditional if you don’t want other gestures to interrupt either.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search