The documentation says set:
recognizer.cancelsTouchesInVew = NO;
and add the recognizer to the top view.
I tried and it does not work.
Below is the code pattern I used:
UIView *custom_overlay_1 = [[UIView alloc] initWithFrame : given_frame];
UIView *custom_overlay_2 = [[UIView alloc] initWithFrame : given_frame];
UITapGestureRecognizer *custom_recognizer_1, *custom_recognizer_2;
custom_recognizer_1 = [[UITapGestureRecognizer alloc]
initWithTarget : self
action :@selector(handle_tap_1:)];
custom_recognizer_2 = [[UITapGestureRecognizer alloc]
initWithTarget : self
action :@selector(handle_tap_2:)];
[custom_recognizer_1 setCancelsTouchesInView: NO];
[custom_recognizer_2 setCancelsTouchesInView: NO];
[custom_overlay_1 addGestureRecognizer: custom_recognizer_1];
[custom_overlay_2 addGestureRecognizer: custom_recognizer_2];
[self.view addSubview custom_overlay_2];
[self.view addSubview custom_overlay_1];
- (int) handle_tap_1 : (UITapGestureRecognizer *) gesture
{
NSLog(@" *** *** handle_tap_1 !!!");
if (gesture.state == UIGestureRecognizerStateRecognized)
NSLog(@" *** *** recognized tap detected in handle_tap_1 !!!");
return (0);
}
- (int) handle_tap_2 : (UITapGestureRecognizer *) gesture
{
NSLog(@" *** *** handle_tap_2 !!!");
if (gesture.state == UIGestureRecognizerStateRecognized)
NSLog(@" *** *** recognized tap detected in handle_tap_2 !!!");
return (0);
}
The problem is custom_overlay_1
is able to catch the tap gesture but custom_overlay_2
doesn’t.
To allow custom_overlay_2
's tap gesture to be regconized, you need to provide custom delegate to custom_recognizer_1
instead and overwrite method gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:)
(simply return true) instead as its docs specify:
Return Value
true
to allow bothgestureRecognizer
andotherGestureRecognizer
to recognize their gestures simultaneously. The default implementation returnsfalse
—no two gestures can be recognized simultaneously.
Setting cancelsTouchesInView = false
doesn't work because it just alter the way touch events be sent to UIView
not to other gesture regconizers, you can read docs of UIGestureRegconizer for more detail:
A window delivers touch events to a gesture recognizer before it delivers them to the hit-tested view attached to the gesture recognizer. Generally, if a gesture recognizer analyzes the stream of touches in a multi-touch sequence and doesn’t recognize its gesture, the view receives the full complement of touches. If a gesture recognizer recognizes its gesture, the remaining touches for the view are canceled. The usual sequence of actions in gesture recognition follows a path determined by default values of the
cancelsTouchesInView
,delaysTouchesBegan
,delaysTouchesEnded
properties:
@interface ViewController : UIViewController<UIGestureRecognizerDelegate>
@end
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
UIView *custom_overlay_1 = [[UIView alloc] initWithFrame : CGRectMake(100, 200, 200, 200)];
[custom_overlay_1 setBackgroundColor:UIColor.redColor];
UIView *custom_overlay_2 = [[UIView alloc] initWithFrame : CGRectMake(0, 0, 100, 100)];
[custom_overlay_2 setBackgroundColor:UIColor.greenColor];
UITapGestureRecognizer *custom_recognizer_1, *custom_recognizer_2;
custom_recognizer_1 = [[UITapGestureRecognizer alloc]
initWithTarget : self
action :@selector(handle_tap_1:)];
[custom_recognizer_1 setDelegate:self];
custom_recognizer_2 = [[UITapGestureRecognizer alloc]
initWithTarget : self
action :@selector(handle_tap_2:)];
[custom_recognizer_1 setCancelsTouchesInView: NO];
[custom_recognizer_2 setCancelsTouchesInView: NO];
[custom_overlay_1 addGestureRecognizer: custom_recognizer_1];
[custom_overlay_2 addGestureRecognizer: custom_recognizer_2];
[self.view addSubview:custom_overlay_1];
[custom_overlay_1 addSubview:custom_overlay_2];
}
- (int) handle_tap_1 : (UITapGestureRecognizer *) gesture
{
NSLog(@" *** *** handle_tap_1 !!!");
if (gesture.state == UIGestureRecognizerStateRecognized)
NSLog(@" *** *** recognized tap detected in handle_tap_1 !!!");
return (0);
}
- (int) handle_tap_2 : (UITapGestureRecognizer *) gesture
{
NSLog(@" *** *** handle_tap_2 !!!");
if (gesture.state == UIGestureRecognizerStateRecognized)
NSLog(@" *** *** recognized tap detected in handle_tap_2 !!!");
return (0);
}
//MARK: UIGestureRecognizerDelegate
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
@end
Also note that if you add custom_overlay_1
and custom_overlay_2
to the same superview, their gesture recognizer can not both receive touch.
superview
/ \
custom_overlay_1 custom_overlay_2
For example, on UI custom_overlay_2
on top of custom_overlay_1
, you tap on custom_overlay_2
, it will be the hit-tested view, the responder chain will be:
custom_overlay_2 -> superview
Therefore custom_overlay_1
and its gesture regconizers can not receive touch events.
To allow both custom_overlay_1
and custom_overlay_2
receive events, you need to make one become subview to another views like in my example code when custom_overlay_2
become subview of custom_overlay_1
. Now responder chain will be ( when tap on custom_overlay_2
):
custom_overlay_2 -> custom_overlay_1 -> superview -> ViewControler...