I have a basic UIView
, with another UIView
nested inside it. Imagine a box with a smaller box inside. It could even be a UIImageView
inside. Just some type of view. Each of the two views needs to have a UITapGestureRecognizer
. When I tap anywhere in the outer view outside of the inner view, I perform one action. When I tap the inner view, I want to do a different action.
Example: I have an outer container view and an inner UIImageView
. If I tap the outer view outside of the inner view, then I segue to the next screen. If I tap the inner image view, then it displays the image as a larger version in a popover.
I'm having difficulty determining the priority. I understand that there is overlap of the UITapGestureRecognizers
where the inner view is, but I want to specify that the the inner view recognizer always takes priority over the outer view.
Apple specifies how to distinguish between different types of gesture recognizers in there docs at https://developer.apple.com/documentation/uikit/touches_presses_and_gestures/coordinating_multiple_gesture_recognizers/preferring_one_gesture_over_another but that doesn't show using the same two gesture recognizers with the same delegate. How is this achievable? Perhaps I'm not understanding the docs correctly.
I'm attaching the gesture recognizers to the views in code and I tried swapping when I add the gesture recognizers to the different views, adding the outside view recognizer before the inner view recognizer, and then the opposite. However, it still only registers the outer view tap gesture recognizer every time.
Below is some code. I have the views created in interface builder with outlets into a particular view. campaignContentView
is the parent view and pictureImageView
and linkDetailsView
are child views. Sometimes I just show a picture which should do one thing. Other times I have a link with an associated thumbnail and tapping on either should do another thing.
@IBOutlet weak var campaignContentView: UIView!
@IBOutlet weak var pictureImageView: UIImageView!
@IBOutlet weak var linkDetailsView: UIView!
private func addNewGestureRecognizer(_ view: UIView) {
let tapView = UITapGestureRecognizer(target: self, action: #selector(CampaignActionContentView.tappedView(_:)))
campaignContentView.addGestureRecognizer(tapView)
switch view {
case linkDetailsView:
let tapLinkDetails = UITapGestureRecognizer(target: self, action: #selector(CampaignActionContentView.tappedLink(_:)))
let tapLinkPicture = UITapGestureRecognizer(target: self, action: #selector(CampaignActionContentView.tappedLink(_:)))
view.addGestureRecognizer(tapLinkDetails)
pictureImageView.addGestureRecognizer(tapLinkPicture)
case pictureImageView:
let tapPicture = UITapGestureRecognizer(target: self, action: #selector(CampaignActionContentView.tappedPicture(_:)))
view.addGestureRecognizer(tapPicture)
default:
print("No new gesture necessary")
}
}
I am also calling bringSubviewToFront(pictureImageView)
and bringSubviewToFront(linkDetailsView)
accordingly. It turns out that the gesture recognizer for linkDetailsView
is actually working correctly, just not the pictureImageView
This does not answer how to specify priority, but I may solve your issue. I don't think you need to use gesture priority unless one view has two gestures attached to it. In your case, you have two views with their own gesture.
I think your real problem is your inner view is not on top of your outer view. I would suggest looking at the view hierarchy debugger. I created a view inside a view and was able to determine their own tap gestures.
I created a simple two box with tap gestures on playground...
import UIKit
import PlaygroundSupport
class MyViewController : UIViewController {
override func loadView() {
let view = UIView()
view.backgroundColor = .white
let innerView = UIView()
innerView.backgroundColor = UIColor.red
view.addSubview(innerView)
innerView.translatesAutoresizingMaskIntoConstraints = false
innerView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
innerView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
innerView.widthAnchor.constraint(equalToConstant: 100).isActive = true
innerView.heightAnchor.constraint(equalToConstant: 100).isActive = true
self.view = view
let outTapGesture = UITapGestureRecognizer(target: self, action: #selector(outterViewTapped))
let innerTapGesture = UITapGestureRecognizer(target: self, action: #selector(innerViewTapped))
view.addGestureRecognizer(outTapGesture)
innerView.addGestureRecognizer(innerTapGesture)
}
@objc func outterViewTapped() {
print("Outter tapped!")
}
@objc func innerViewTapped() {
print("Inner tapped!")
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()