当您的应用程序接收到一个触摸事件时,将启动一个命中测试过程以确定哪个视图应该接收该事件。该过程从视图层次结构的根开始,通常是应用程序的窗口,并按从前到后的顺序搜索子视图,直到找到触摸下的最前面的视图。该视图成为命中测试视图并接收触摸事件。此过程中涉及的每个视图首先测试事件位置是否在其范围内。只有在测试成功后,视图才会将事件传递给子视图以进行进一步的命中测试。因此,如果您的视图处于触摸状态但位于其父视图边界之外,则父视图将无法测试事件位置并且不会将触摸事件传递给您的视图。
解决此问题的一种方法是修改视图层次结构的布局,以便您的视图位于其父视图的边界内。如果由于某些原因必须维护现有布局,您可以更改父视图的命中测试行为,以便它不会忽略触摸事件。这可以通过覆盖父视图类的 -(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。应用程序调用了它的 beginIgnoringInteractionEvents 方法,而没有对其 endIgnoringInteractionEvents 方法的匹配调用。您应该确保 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
}
}