I have a UIImageView
(userInteractionEnabled) UIButton
and UITextField
both are subviews of superview, which has a UITapGestureRecognizer
add to it. So my superview
and UIImageView
are responds to tap gestures but UIButton
and UITextField
ignores it. Is it a policy of UITapGestureRecognizer added to superview of this is my fault? Does anyone know about this situation?
This is my code with view subviews and gestureRecognizer
@objc func tapBlurButton(_ sender: UITapGestureRecognizer) {
print("Please Help!")
endEditing(true)
}
override init(frame: CGRect) {
super.init(frame: frame)
// backgroundColor = UIColor.green
addSubview(logoImageView)
// addSubview(skipButton)
addSubview(emailTextField)
addSubview(passwordTextField)
addSubview(loginButton)
observeKeyboardNotifications()
// logoImageView.translatesAutoresizingMaskIntoConstraints = false
logoImageView.anchorWithConstantsToTop(centerYAnchor, left: nil , bottom: nil, right: nil, topConstant: -230, leftConstant: 0, bottomConstant: 0, rightConstant: 0)
logoImageView.widthAnchor.constraint(equalToConstant: 160).isActive = true
logoImageView.heightAnchor.constraint(equalToConstant: 160).isActive = true
logoImageView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
emailTextField.anchorWithConstantsToTop(logoImageView.bottomAnchor, left: leftAnchor, bottom: nil, right: rightAnchor, topConstant: 8, leftConstant: 32, bottomConstant: 0, rightConstant: 32)
emailTextField.heightAnchor.constraint(equalToConstant: 50).isActive = true
passwordTextField.anchorWithConstantsToTop(emailTextField.bottomAnchor, left: leftAnchor, bottom: nil, right: rightAnchor, topConstant: 8, leftConstant: 32, bottomConstant: 0, rightConstant: 32)
passwordTextField.heightAnchor.constraint(equalToConstant: 50).isActive = true
loginButton.anchorWithConstantsToTop(passwordTextField.bottomAnchor, left: leftAnchor, bottom: nil, right: rightAnchor, topConstant: 16, leftConstant: 32, bottomConstant: 0, rightConstant: 32)
loginButton.heightAnchor.constraint(equalToConstant: 50).isActive = true
isUserInteractionEnabled = true
addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(LoginCell.tapBlurButton(_:))))
isUserInteractionEnabled = true
}
I saw other answers they are just tells how to workaround it , but they don't thouch on real reason why it happens, moreover no one refers to docs that tell about this issue exactly. So my question does not asks how to avoid it or how to resolve it, i want to know why it happens, and am i right when i suspect UIkit of doing that so this not my fault or i am wrong a had missed something?
There is a really simple and helpful article from Apple. And to understand what you want you should look at the Responder Chain concept.
Understanding Event Handling, Responders, and the Responder Chain
Basically, if you want to understand why your UIImageView
responds to taps, but UIButton
and UITextField
don't do it, there is a simple explanation. UIButton
and UITextField
are UIControl
's subclasses, but UIImageView
is not, it's only UIResponder
's subclass. So, every UIControl
uses The Target-Action Mechanism, but a UIResponder
doesn't do it.
And the documentation says:
Controls communicate directly with their associated target object using action messages. When the user interacts with a control, the control calls the action method of its target object—in other words, it sends an action message to its target object. Action messages are not events, but they may still take advantage of the responder chain. When the target object of a control is nil, UIKit starts from the target object and walks the responder chain until it finds an object that implements the appropriate action method.
So, it means that by default when you click on a UIControl
object, it doesn't propagate that event to the superview to handle it. Controls override default UIResponder
behavior. But when you click on a UIResponder
, it sends the event to the superview to handle it, the touch falls through. So, UIKit
does that and usual UITapGestureRecognizer
s don't go through UIControl
s by default, since controls have their set of preset events and use another mechanism of handling them. But when you disable UIControl
's ability to interact with user taps, your superview tap gesture recognizer will respond.
In your case, if you set that, your func tapBlurButton(_ sender: UITapGestureRecognizer)
will be called even if you press the button, because that button won't try to handle the event and the touch will just fall through to the superview.
loginButton.isUserInteractionEnabled = false