2つのビューがありますが、1つのビューを(実質的に)大きくしたいと思います。 tapGestureをv1に配置すると、タップジェスチャはより大きなヒット領域で機能しますが、tapGestureをv2に配置すると、機能しません(実際には、元の境界内でなくても、tapGestureをまったく認識しません)。 TestView1のヒットテストメソッドをループすると、ポイントがフレームに含まれます。
#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
ビュー階層をトラバースしていません。ドキュメントから:
このメソッドは、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のタッチ可能な領域を拡張することに成功しており、上記のコードを使用すると、v1のサブビューとしてv2でそれを行うことができます。
ただし、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];
V2はタッチイベントを受信しません。 v1の周囲の領域をクリックすると、- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
にself
が返されるためです。これは、宛先であるヒットテストビューである「v1」であることを宣言したことを意味します。すべてのタッチイベントの。
v1のタッチ可能な領域を拡張する正しい方法は、_TestView1
_および_TestView2
_に- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
を実装することです。
_- (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の周囲の領域をクリックすると、「はい、あなたは私に触れました。誰がそれを処理できるかを確認します。多分それは私です、多分それは私のサブビューの1つです」と宣言します。したがって、ヒットテストは続行され、v1はそのサブビューv2が最上位のビューであることがわかります。したがって、v2はクリックイベントの宛先です。
V2が1つであることをv1がどのように知ることができるかを尋ねることができます。トリックを明らかにするための擬似コードは次のとおりです。
_@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];
_の- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
で_TestView1
_を呼び出すと、ポイントがv2の領域にあるかどうかを尋ねられます。 v2の答えが「はい」で、サブビューがないため、v2は- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
に戻ります。
それがすべての話です。
あなたがそれをしている方法は可能ですが、正しく行うのは難しいです。
私がお勧めするのは、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;
}
...
}
ビューに共通のスーパービューがない場合は、それぞれをより大きな透明なビューに埋め込み、この大きなビューに認識機能を追加します。
メソッドを実装する必要があります:
- (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を返す場合、サブビューの階層がトラバースされます。それ以外の場合、ビュー階層のブランチは無視されます。このメソッドを自分で呼び出す必要はめったにありませんが、サブビューからタッチイベントを非表示にするためにオーバーライドすることができます。
このメソッドは、非表示になっている、ユーザーインタラクションを無効にしている、またはアルファレベルが0.01未満のビューオブジェクトを無視します。この方法では、ヒットを決定するときにビューのコンテンツは考慮されません。したがって、指定されたポイントがそのビューのコンテンツの透明な部分にある場合でも、ビューを返すことができます。
あなたの質問から私が理解する限り、あなたはV1の上に大きなV2を追加しました。したがって、V2はV1の境界内でのみタッチ可能になります。そのため、V2の余分な領域ではジェスチャーが認識されません。