我有2个视图,但我想让1个视图(虚拟)更大。 如果我将tapGesture放在v1上,则点击手势可以使用更大的命中区域 但是如果我将我的tapGesture放在v2上它就不起作用(实际上它根本不识别tapGesture,甚至不在原始边界内),即使我循环通过我的TestView1 hittest方法并且点数得到控制在框架中。
#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
答案 0 :(得分:10)
您没有遍历视图层次结构。来自文档:
此方法通过向每个子视图发送pointInside:withEvent:消息来遍历视图层次结构,以确定哪个子视图应该接收触摸事件。如果pointInside:withEvent:返回YES,则遍历子视图的层次结构;否则,忽略视图层次结构的分支。
您只需要实施pointInside:withEvent:
。您不应该覆盖hitTest:withEvent:
,因为标准实现将调用您的pointInside:withEvent:
实现,并为您完成遍历层次结构的所有艰苦工作。
像这样实施:
@interface TestView1 : UIView
@end
@implementation TestView1
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
CGFloat radius = 100.0;
CGRect frame = CGRectMake(0, 0,
self.frame.size.width + radius,
self.frame.size.height + radius);
return (CGRectContainsPoint(frame, point));
}
@end
@interface TestView2 : UIView
@end
@implementation TestView2
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
CGFloat radius = 100.0;
CGRect frame = CGRectMake(0, 0,
self.frame.size.width + radius,
self.frame.size.height + radius);
return (CGRectContainsPoint(frame, point));
}
@end
现在,您是否需要这两种观点取决于您。您已经成功扩展了v1的可触摸区域,使用上面的代码,您可以使用v2作为v1的子视图。
但是,TestView1
和TestView2
实现完全相同(即使在您的原始帖子中),那么为什么需要将它们分开?您可以使v1和v2成为同一类的两个实例,例如TestView
,并按如下方式实例化它们:
TestView *v1 = [[TestView alloc] initWithFrame:CGRectMake(50.f, 50.f, 100.f, 100.f)];
[self.view addSubview:v1];
v1.clipsToBounds = YES;
TestView *v2 = [[TestView alloc] initWithFrame:CGRectMake(0.f, 0.f, 100.f, 100.f)];
v2.backgroundColor = UIColor.yellowColor;
[v1 addSubview:v2];
答案 1 :(得分:5)
您的v2不会收到任何触摸事件。因为当你单击v1周围的区域时,它会在self
中返回- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
,这意味着你已经声明它是“v1”,即命中测试视图,谁是所有的目的地触摸事件。
扩展v1可触摸区域的正确方法是在- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
和TestView1
中实施TestView2
:
- (BOOL)pointInside:(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 YES;
}
return [super pointInside:point withEvent:event];
}
上面的代码意味着,当你点击v1周围的区域时,它会声明“是的,你已经触动了我。我会检查谁可以处理它。也许是我,也许这是我的一个子视图”。所以命中测试继续,v1会发现它的子视图v2是最顶层的视图,因此v2是你点击事件的目的地。
你可能会问v1怎么知道v2就是那个。这是揭示诀窍的伪代码:
@implementation UIView
//...
//...
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
return CGRectContainsPoint(self.bounds, point); // Honestly tell others if the point is inside the bounds. That's the normal case.
}
// This method returns a hit-test view who or whose gesture recognizer is responsible for handling the events
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
for(UIView *aSubview in self.subviews)
{
// Ask each subview if the point falls in its area.
if ([aSubview pointInside:[self convertPoint:point toView:aSubview] point withEvent:event])
{
return [aSubview hitTest:[self convertPoint:point toView:aSubview] withEvent:event];
}
}
// If no one can handle the event.
return self;
}
//...
//...
@end
为简单起见,这些代码未考虑userInteractionEnable
,alpha
和其他因素
当您在[super pointInside:point withEvent:event];
的{{1}}中致电TestView1
时,会询问该点是否位于v2的区域内。如果v2的答案是肯定的,并且因为它没有任何子视图,那么v2将在其- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
中返回。
这就是故事。
答案 2 :(得分:2)
据我所知,你在V1之上添加了更大的V2。所以V2只能在V1的范围内触摸。因此,在V2的额外区域中无法识别您的手势。
答案 3 :(得分:2)
您需要实现方法:
- (BOOL)pointInside:(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 YES;
}
return [super pointInside:point withEvent:event];
}
然后:
- (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 [super hitTest:point withEvent:event;
}
来自文档
此方法通过发送遍历视图层次结构 pointInside:withEvent:消息到每个子视图以确定哪个 子视图应该会收到触摸事件。如果pointInside:withEvent: 返回YES,然后遍历子视图的层次结构;否则,它 视图层次结构的分支被忽略。你很少需要打电话 方法你自己,但你可以覆盖它来隐藏触摸事件 子视图。
此方法忽略已隐藏且已禁用的视图对象 用户交互,或者alpha级别小于0.01。这种方法 在确定点击时不会考虑视图的内容。 因此,即使指定的点在a中,仍然可以返回视图 该视图内容的透明部分。
答案 4 :(得分:2)
你这样做的方式是可能的,但很难做到正确。
我建议将UITapGestureRecognizer
添加到他们的公共超级视图中,然后根据点击位置决定接收者的视图,例如
- (void)onTap:(UITapGestureRecognizer*)tapRecognizer {
CGPoint touchPosition = [tapRecognizer locationInView:self];
CGRect view1Frame = self.view1.frame;
view1Frame.width += 100;
view1Frame.height += 100;
if (CGRectContainsPoint(view1Frame, touchPosition)) {
[self.view1 handleTap];
return;
}
...
}
如果视图没有共同的超视图,只需将每个视图嵌入更大的透明视图中,并将识别器添加到更大的视图中。