アプリケーションがタッチイベントを受信すると、ヒットテストプロセスが開始され、イベントを受信するビューを決定します。このプロセスは、ビュー階層のルート(通常はアプリケーションのウィンドウ)から始まり、タッチの下で最前面のビューが見つかるまで、前後の順序でサブビューを検索します。そのビューがヒットテストビューになり、タッチイベントを受け取ります。このプロセスに関係する各ビューは、最初にイベントの場所がその範囲内にあるかどうかをテストします。テストが成功した後でのみ、ビューはイベントをサブビューに渡して、さらにヒットテストを行います。したがって、ビューがタッチされているが、親ビューの境界の外側にある場合、親ビューはイベントの場所のテストに失敗し、タッチイベントをビューに渡しません。
この問題の解決策の1つは、ビューが親ビューの境界内に収まるようにビュー階層のレイアウトを変更することです。何らかの理由で既存のレイアウトを維持する必要がある場合は、親ビューのヒットテストの動作を変更して、タッチイベントを無視しないようにすることができます。これは、親ビューのクラスの-(UIView *)hitTest:withEvent:メソッドをオーバーライドすることで実行できます。
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
// Convert the point to the target view's coordinate system.
// The target view isn't necessarily the immediate subview
CGPoint pointForTargetView = [self.targetView convertPoint:point fromView:self];
if (CGRectContainsPoint(self.targetView.bounds, pointForTargetView)) {
// The target view may have its view hierarchy,
// so call its hitTest method to return the right hit-test view
return [self.targetView hitTest:pointForTargetView withEvent:event];
}
return [super hitTest:point withEvent:event]; }
この問題の他の考えられる原因は次のとおりです。
ビューまたはその親ビューのuserInteractionEnabledプロパティはNOに設定されます。アプリケーションは、endIgnoringInteractionEventsメソッドへの一致する呼び出しなしで、beginIgnoringInteractionEventsメソッドを呼び出しました。ビューをインタラクティブにする場合は、userInteractionEnabledプロパティがYESに設定されていること、およびアプリケーションがユーザーイベントを無視しないことを確認する必要があります。
ビューがアニメーション中にタッチイベントを受け取らない場合、それはUIViewのアニメーションメソッドが通常、アニメーションの進行中にタッチイベントを無効にするためです。UIViewアニメーションを開始するときに、UIViewAnimationOptionAllowUserInteractionオプションを適切に構成することで、この動作を変更できます。
別の解決策は
ビューに独自のカスタム実装を追加します
class CustomView: YourParentView {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
super.hitTest(point, with: event)
return overlapHitTest(point: point, withEvent: event)
}
}
extension UIView {
func overlapHitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
// 1
if !self.isUserInteractionEnabled || self.isHidden || self.alpha == 0 {
return nil
}
// 2
var hitView: UIView? = self
if !self.point(inside: point, with: event) {
if self.clipsToBounds {
return nil
} else {
hitView = nil
}
}
// 3
for subview in self.subviews.reversed() {
let insideSubview = self.convert(point, to: subview)
if let sview = subview.overlapHitTest(point: insideSubview, withEvent: event) {
return sview
}
}
return hitView
}
}