web-dev-qa-db-ja.com

タッチを下のUIViewsに渡す

UIViewには4つのボタンがあり、ボタンビューの上部には別のUIViewがあります。一番上のビューには、UIImageViewが付いたUITapGestureRecognizerが含まれています。

私が作成しようとしている動作は、ユーザーがUIImageViewをタップすると、画面の右下隅にある小さいサイズと、大きくなるようにアニメートするモードを切り替えることです。大きい場合は底面図のボタンを無効にし、小さい場合は右下隅にあるボタンにタッチを通過させ、ボタンが通常どおり動作するようにします。私はほとんどそこにいますが、トップビューのUserInteractionsを無効にしない限り、タッチをボタンに渡すことができません。

トップビューのinitWithFrame:にこれがあります:

// Add a gesture recognizer to the image view
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(imageTapped:)];
tapGestureRecognizer.cancelsTouchesInView = NO;
[imageView addGestureRecognizer:tapGestureRecognizer];
[tapGestureRecognizer release];

そして私はこれが私のimageTapped:メソッドです:

- (void) imageTapped:(UITapGestureRecognizer *) gestureRecognizer {
    // Toggle between expanding and contracting the image
    if (expanded) {
        [self contractAnimated:YES];
        expanded = NO;
        gestureRecognizer.cancelsTouchesInView = NO;
        self.userInteractionEnabled = NO;
        self.exclusiveTouch = NO;
    }
    else {
        [self expandAnimated:YES];
        expanded = YES;
        gestureRecognizer.cancelsTouchesInView = NO;
        self.userInteractionEnabled = YES;
        self.exclusiveTouch = YES;
    }
}

上記のコードでは、画像が大きい場合、ボタンは非アクティブになり、画像に触れると縮小し、ボタンがアクティブになります。ただし、小さな画像はタッチを受けないため、拡大しません。

両方の場合にself.userInteractionEnabled = YESを設定すると、タッチすると画像が伸縮しますが、ボタンはタッチを受け取らず、無効のように動作します。

タッチされたときに画像を拡大および縮小するために、画像が縮小状態にある場合にのみ下のボタンがタッチを受け取るために離れていますか?私はここで何か愚かなことをしていて、明らかなことを見逃していますか?

私はこれが機能するようにしようとして絶対に怒っていますので、どんな助けも感謝します、

デイブ

更新:

さらにテストするために、touchesBegan:メソッドとtouchesCancelled:メソッドをオーバーライドし、UIImageViewを含むビューでそれらのスーパー実装を呼び出しました。上記のコードでは、touchesCancelled:は呼び出されず、touchesBegan:は常に呼び出されます。

そのため、ビューがタッチを取得しているように見えますが、それらは下のビューに渡されません。

[〜#〜] update [〜#〜]

これは、レスポンダーチェーンの動作方法によるものですか?私のビュー階層は次のようになります。

VC - View1
     -View2
      -imageView1 (has tapGestureRecogniser)
      -imageView2
     -View3
      -button1
      -button2

OSは最初にView2が前面にあるとヒットテストを行うため、すべてのタッチを取得する必要があり、View2のuserInteractionsがNOに設定されていない限り、これらはView3に渡されません。その場合、imageView1もタッチを受信できなくなります。これはどのように機能しますか?また、View2がView3へのタッチを通過する方法はありますか?

64

UIGestureRecognizerは私が思うにニシンです。これを解決するために、最後にpointInside:withEvent:私のメソッドUIView:

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
    BOOL pointInside = NO;

    if (CGRectContainsPoint(imageView.frame, point) || expanded) pointInside = YES;

    return pointInside;
}

これにより、imageViewをタッチするか、その展開フラグが設定されている場合、ビューはすべてのタッチをトラップします。展開されていない場合、タッチがimageView上にある場合にのみタッチをトラップします。

NOを返すことにより、トップレベルVCのビューは、ヒットを探してビュー階層の残りの部分を照会します。

91

ViewまたはStoryboardXIBを選択して...


enter image description here


またはSwiftの場合

view.isUserInteractionEnabled = false
59
Michael

IGestureRecognizerDelegate Protocol を調べます。具体的には、gestureRecognizer:shouldReceiveTouch:

UIGestureRecognizerUIViewControllerのプロパティにする必要があります。

// .h
@property (nonatomic, strong) UITapGestureRecognizer *lowerTap;

// .m
@synthesize lowerTap;

// When you are adding the gesture recognizer to the image view
self.lowerTap = tapGestureRecognizer

UIViewControllerをデリゲートにしてください。

[self.lowerTap setDelegate: self];

そして、あなたはこのようなものを持っているでしょう、

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    if (expanded && gestureRecognizer == self.lowerTap) {    
        return NO;
    }
    else {
        return YES;
    }
}

もちろん、これは正確なコードではありません。しかし、これは一般的なパターンです。

6
john

@Magic Bullet Daveのソリューションですが、Swiftで

スイフト3

override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
    var pointInside = false
    if commentTextField.frame.contains(point) {
        pointInside = true
    } else {
        commentTextField.resignFirstResponder()
    }
    return pointInside
}

ImagePickerViewController.cameraOverlayのCameraOverlayViewで使用して、ユーザーが新しい写真を撮影しながらコメントできるようにします

3
imike

別の解決策があります。私には2つのビューがあり、それらをオーバーラップしていたCustomSubViewと呼びましょう。両方ともタッチを受け取るはずです。そこで、View ControllerとカスタムUIViewクラスを用意し、インターフェイスビルダーで設定したViewControllerViewと呼んで、そのビューへのタッチを受け取る2つのビューを追加しました。

そこで、hitTestを上書きしてViewControllerViewのタッチをインターセプトしました。

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    return self;
}

それからViewControllerViewで上書きしました:

- (void)touchesBegan:(NSSet *)touches
           withEvent:(UIEvent *)event
{
    [super touchesBegan:touches withEvent:event];
    for (UIView *subview in [self.subviews reverseObjectEnumerator])
    {
        if ([subview isKindOfClass:[CustomSubView class]])
        {
            [subview touchesBegan:touches withEvent:event];
        }
    }
}

touchesMovedtouchesEndedおよびtouchesCancelledでもまったく同じことを行います。

3
Nico S.
// this is due to userInteractionEnabled deficiency: the flag stops traversal of subviews
@objc(YourObjcPrefixHereForXibAndStoryboardUseViewTransparentToTouch)
open class ViewTransparentToTouch: UIView
{
public var daisyView: UIView?
override open func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
    let hitView = super.hitTest(point, with: event)
    if hitView === self {
        return daisyView
    }
    return hitView // childView
}
}
0
Anton Tropashko