web-dev-qa-db-ja.com

タッチイベントを無視して別のサブビューのUIControlオブジェクトに渡す方法は?

UIViewが画面の隅を占めるカスタムUIViewControllerがありますが、ボタンやその他のものがある部分を除き、ほとんどは透明です。そのビュー上のオブジェクトのレイアウトにより、ビューのフレームはその下のいくつかのボタンを覆うことができます。重要なものに触れていない場合、そのビューへのタッチを無視できるようにしたいのですが、実際のタッチイベント(touchesEnded/nextResponderのもの)のみを渡すことができるようです。 UIButtonまたはtouchesEndedを使用しないようなものがある場合、それにタッチイベントを渡すにはどうすればよいですか?

このカスタムViewControllerはさまざまなビューで使用できるため、呼び出すボタンセレクターを手動で見つけることはできません。基本的にこれを呼び出す方法が必要です:

[self.nextResponder touchesEnded:touches withEvent:event];

uIControlタイプでも同様です。

64
Chris C

おそらくこれを行う最良の方法は、hitTest:withEvent:タッチを無視したいビューで。ビュー階層の複雑さに応じて、これを行う簡単な方法がいくつかあります。

無視するビューの下にビューへの参照がある場合:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    UIView *hitView = [super hitTest:point withEvent:event];

    // If the hitView is THIS view, return the view that you want to receive the touch instead:
    if (hitView == self) {
        return otherView;
    }
    // Else return the hitView (as it could be one of this view's buttons):
    return hitView;
}

ビューへの参照がない場合:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    UIView *hitView = [super hitTest:point withEvent:event];

    // If the hitView is THIS view, return nil and allow hitTest:withEvent: to
    // continue traversing the hierarchy to find the underlying view.
    if (hitView == self) {
        return nil;
    }
    // Else return the hitView (as it could be one of this view's buttons):
    return hitView;
}

最初のアプローチが最も堅牢であることをお勧めします(基になるビューへの参照を取得できる場合)。

131
Stuart

迅速な回答:

UIViewサブクラスIgnoreTouchViewを作成できます。ストーリーボードで、タッチを通過させるVCのビューに設定します。

class IgnoreTouchView : UIView {
    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        let hitView = super.hitTest(point, with: event)
        if hitView == self {
            return nil
        }
        return hitView
    }
}

問題のUIViewに対して、UserInteractionEnabledをtrueに設定する必要があることに注意してください!

11
Kevin

また、ユーザーの操作を無効にして、タッチイベントが無視され、親に渡されるようにすることもできます。

In Swift:

yourView.isUserInteractionEnabled = false
9
Melkon

オブジェクト間でUITouchイベントを渡す良い方法が見つかりませんでした。通常は、hitTestを実装し、ポイントが処理したいビューにない場合はnilを返すのがより適切です。

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
7
EricS

これは古い質問であり、検索の上位に表示されます。だから、私はあなたの状況により良いかもしれない別の可能な解決策を投稿したいと思った。私の場合、別のラベルの上にラベルをドラッグし、hitTestで下のラベルを取得したかったのです。最も簡単なことは、userInteractionEnabledNOまたはfalseに設定し、hitTestを実行してから、再度移動する必要がある場合に再度設定することです。 Swiftのコードサンプルを次に示します。

override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
    let touch = touches.first
    let location = touch?.locationInView(self.view)
    self.label1.userInteractionEnabled = false
    let view = self.view.hitTest(location!, withEvent: nil) as? UILabel
    print(view?.text)
    self.label1.userInteractionEnabled = true
}
3
smileBot

私の問題は似ていました:複雑なタッチレスポンダーを備えたUIControlがありましたが、スクロールビューに埋め込まれたときにおかしくなりました...

これにより、サブビューにアクティブなタッチジェスチャがある場合、親のスクロールが防止されます(または、他の対話を防止するように変更できます)。

私の解決策:

サブビューでdidBeginInteractionおよびdidEndInteractionのデリゲートプロトコルを作成します。

TouchesBeganからdelegate.didBeginInteractionを呼び出す

TouchesEndedおよびtouchesCancelledからdelegate.didEndInteractionを呼び出します

親コントローラーで、didBeginおよびdidEndプロトコルを実装し、デリゲートを設定します

次に、didBeginでscrollView.scrollEnabled = NOを設定し、didEndでscrollEnabled = YESを設定します。

これが、提起された質問とは少し異なるユースケースを持つ人を助けることを願っています!

0
Lytic