12

私は2つのビューを持っていますが、1つのビューを(仮想的に)大きくしたいです。v1にtapGestureを配置すると、タップジェスチャはより大きなヒット領域で機能しますが、v2にtapGestureを配置すると機能しません(実際には、元の境界内になくても、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
4

5 に答える 5

11

ビュー階層をたどっていません。ドキュメントから:

このメソッドは、各サブビューに 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];
于 2013-08-08T09:08:20.600 に答える
5

v2 はタッチ イベントを受け取りません。これは、すべてのタッチ イベントの宛先であるヒット テスト ビューである「v1」であることを宣言したことを意味しますself。 v1 のタッチ可能な領域を拡張する正しい方法は、 andに実装することです。- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)eventTestView1TestView2

- (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 が v2 であることを 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

これらのコードは、簡単にするためuserInteractionEnablealphaやその他のことを考慮していません。の
を呼び出すと、ポイントが v2 の領域にあるかどうか尋ね[super pointInside:point withEvent:event];られます。v2 の答えが「はい」で、サブビューがない場合、v2 はその. TestView1- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event

それがすべての話です。

于 2013-08-08T09:08:20.600 に答える
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 を返す場合、サブビューの階層がトラバースされます。それ以外の場合、ビュー階層のブランチは無視されます。このメソッドを自分で呼び出す必要はほとんどありませんが、サブビューからタッチ イベントを非表示にするためにオーバーライドする場合があります。

このメソッドは、非表示のビュー オブジェクト、ユーザー操作が無効になっているビュー オブジェクト、またはアルファ レベルが 0.01 未満のビュー オブジェクトを無視します。このメソッドは、ヒットを判断するときにビューのコンテンツを考慮しません。したがって、指定されたポイントがそのビューのコンテンツの透明な部分にある場合でも、ビューを返すことができます。

于 2013-08-08T08:25:11.083 に答える
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;
    }

    ...
}

ビューに共通のスーパービューがない場合は、それぞれをより大きな透明なビューに埋め込み、認識機能をこのより大きなビューに追加します。

于 2013-08-08T09:31:26.593 に答える
2

あなたの質問から私が理解している限りでは、V1 の上にさらに大きな V2 を追加しました。したがって、V2 は V1 の境界内でのみタッチ可能になります。そのため、V2 のエクストラ エリアではジェスチャーが認識されません。

于 2013-08-05T14:22:38.153 に答える