I have 2 views , but i want to make 1 view (virtually) bigger. if I place my tapGesture on v1, the tap gesture works with a bigger hit area but if I place my tapGesture on v2 it doesn't work ( actually it doesn't recognizes the tapGesture at all, even not inside the original bounds ) even though i loop through my TestView1 hittest method and the points get contained in the frame.
#import "ViewController.h"
@interface TestView1 : UIView
@end
@implementation TestView1
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
CGFloat radius = 100.0;
CGRect frame = CGRectMake(0, 0,
self.frame.size.width + radius,
self.frame.size.height + radius);
if (CGRectContainsPoint(frame, point)) {
return self;
}
return nil;
}
@end
@interface TestView2 : UIView
@end
@implementation TestView2
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
CGFloat radius = 100.0;
CGRect frame = CGRectMake(0, 0,
self.frame.size.width + radius,
self.frame.size.height + radius);
if (CGRectContainsPoint(frame, point)) {
return self;
}
return nil;
}
@end
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
TestView1 *v1 = [[TestView1 alloc] initWithFrame:CGRectMake(50.f, 50.f, 100.f, 100.f)];
[self.view addSubview:v1];
TestView2 *v2 = [[TestView2 alloc] initWithFrame:CGRectMake(0.f, 0.f, 100.f, 100.f)];
v2.backgroundColor = UIColor.yellowColor;
[v1 addSubview:v2];
UITapGestureRecognizer *gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(panGesture:)];
[v2 addGestureRecognizer:gesture];
}
- (void) panGesture:(UIPanGestureRecognizer *)recognizer
{
NSLog(@"tap");
}
@end
As far as I understand from your question, you have added bigger V2 on top of V1. so V2 will be touchable only with in the bounds of V1. So your gesture is not recognised in V2's extra area.
You're not traversing the view hierarchy. From the documentation:
You only need to implement
pointInside:withEvent:
. You shouldn't overridehitTest:withEvent:
because the standard implementation will call your implementation ofpointInside:withEvent:
and do for you all the hard work of traversing the hierarchy.Implement like this:
Now, whether you need both the views is up to you. You had already succeeded in expanding touchable area of v1, and with the code above you can do it with v2 as a subview of v1.
However,
TestView1
andTestView2
implementations are exactly the same (even in your original post), so why do you need to have them separate? You could make v1 and v2 both instances of the same class, e.g.TestView
, and instantiate them as follows:You need to implement method:
And then:
From documentation
The way you are doing it is possible but difficult to do correctly.
What I recommend is to add the
UITapGestureRecognizer
to their common superview and then decide what view is the receiver depending on tap location, e.g.If the views don't have a common superview, just embed each of them in a bigger transparent view and add the recognizer to this bigger view.
Your v2 won't receive any touch events. because when you click the area around your v1, it returns
self
in its- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
, which means you have declared that it is "v1", the hit-test view, who is the destination of all the touch events.The right way to expand your v1's touchable ares is to implement
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
in yourTestView1
andTestView2
:The code above means that, when you click the area around your v1, it declares "Yes, you have touched me. And I will check who can handle it. Maybe it's me, maybe it's one of my subviews". So the hit-test continues and v1 will find its subview v2 is the top-most view thus v2 is the destination of your click event.
You may ask how could v1 know that v2 is the one. Here is the pseudo code to reveal the trick:
These code have not taken
userInteractionEnable
,alpha
and other things into account, for simplicity.When you call
[super pointInside:point withEvent:event];
in yourTestView1
's- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
, it ask if the point falls in v2's area. If v2's answer is yes and because it doesn't have any subviews, so v2 will return itself in its- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
.That's all the story.