I have a question regarding UITapGestureRecognizer, its configuration, and how it affects the responder chain.
Here’s the situation: I have a UITapGestureRecognizer added to the window, and there is also a UIBarButtonItem. I noticed that depending on the gesture recognizer’s settings, it influences the responder chain in unexpected ways.
I created a minimal reproducible example (MRE) and included a table with print outputs at the bottom. For the first three configuration options, everything works as expected. However, the last two options cause confusion: If the UITapGestureRecognizer is configured as:
cancelsTouchesInView = false
delaysTouchesBegan = true
the tap is recognized on the window.
If configured as:
cancelsTouchesInView = true
delaysTouchesBegan = true
the tap is recognized on the UIBarButtonItem.
My question is: why does this happen?
I expected that delaysTouchesBegan and cancelsTouchesInView would only affect the gesture recognizer to which they are applied, controlling whether the tap is forwarded and whether gestures are handled simultaneously. However, it appears that these settings influence the entire responder chain.
STEPS TO REPRODUCE
final class ViewController: UIViewController {
private lazy var tapGestureRecognizer = UITapGestureRecognizer(target: self,
action: #selector(tapOnWindow))
private lazy var menuBarButton = UIBarButtonItem(image: .init(systemName: "list.bullet")!,
style: .plain,
target: self,
action: #selector(tapOnMenu))
override func viewDidLoad() {
super.viewDidLoad()
// Option one
// no-op
// Option two
// tapGestureRecognizer.cancelsTouchesInView = false
// Option three
// tapGestureRecognizer.delaysTouchesBegan = true
// Option four
// tapGestureRecognizer.cancelsTouchesInView = false
// tapGestureRecognizer.delaysTouchesBegan = true
// Option five
// tapGestureRecognizer.cancelsTouchesInView = true
// tapGestureRecognizer.delaysTouchesBegan = true
title = "Test"
UIApplication.shared.windows.first?.addGestureRecognizer(tapGestureRecognizer)
navigationItem.leftBarButtonItems = [menuBarButton]
navigationController?.navigationBar.tintColor = .black
}
@objc private func tapOnWindow(_ sender: UITapGestureRecognizer) {
print("Tap on window")
}
@objc private func tapOnMenu() {
print("Tap on menu")
}
}
/*
+---------+----------------+
| Option | Action |
+---------+----------------+
| 1 | Tap on menu |
+---------+----------------+
| 2 | Tap on window |
| | Tap on menu |
+---------+----------------+
| 3 | Tap on menu |
+---------+----------------+
| 4 | Tap on window |
+---------+----------------+
| 5 | Tap on menu |
+---------+----------------+
*/