【问题标题】:Detect if certain UIView was touched amongst other UIViews检测是否在其他 UIView 中触摸了某些 UIView
【发布时间】:2011-02-17 02:32:22
【问题描述】:

我有 3 个 UIView,位于一个大的 uiview 之上。我想知道用户是否触摸了顶部而不关心其他的。我将在第二个 UIView 中有几个按钮,在第三个 UIView 中有一个 UITable。

问题是我在第一个视图上打开了 userInteractionEngabled 并且有效,但是即使我将其关闭,所有其他视图也会以相同的方式响应。如果我在 self.view 上禁用 userInteractionEnabled,他们都没有响应。我也无法检测到 touchesBegan 委托方法中触摸了哪个视图。

我的代码:

UIView *aView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 150)];
aView = userInteractionEnabled = YES;
[self.view addSubview:aView];

UIView *bView = [[UIView alloc] initWithFrame:CGRectMake(0, 150, 320, 50)];
bView.userInteractionEnabled = NO;
[self.view addSubview:bView];

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
//This gets called for a touch anywhere
}

【问题讨论】:

    标签: iphone uiview uitouch


    【解决方案1】:

    在我的情况下,hitTest 没有返回我想要找到的 UIView。但是下面的代码效果更好:

        CGPoint locationPoint = [[touches anyObject] locationInView:self.view];
        CGPoint viewPoint = [myImage convertPoint:locationPoint fromView:self.view];
        if ([myImage pointInside:viewPoint withEvent:event]) {
           // do something
        }
    

    【讨论】:

    • 我也遇到了接受答案的问题。无论出于何种原因,这也很有效。请注意,我还试图检测 UIImageView 上的命中,而不仅仅是 UIView(如果相关的话)。
    【解决方案2】:

    当您的应用程序接收到触摸事件时,会启动一个命中测试过程以确定哪个视图应该接收该事件。该过程从视图层次结构的根开始,通常是应用程序的窗口,并按从前到后的顺序搜索子视图,直到找到触摸下的最前面的视图。该视图成为命中测试视图并接收触摸事件。此过程中涉及的每个视图首先测试事件位置是否在其范围内。只有在测试成功后,视图才会将事件传递给子视图以进行进一步的命中测试。因此,如果您的视图处于触摸状态但位于其父视图的边界之外,则父视图将无法测试事件位置并且不会将触摸事件传递给您的视图。

    解决此问题的一种方法是修改视图层次结构的布局,使您的视图位于其父视图的边界内。如果由于某些原因必须维护现有布局,您可以更改父视图的命中测试行为,以便它不会忽略触摸事件。这可以通过覆盖父视图类的 -(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
        }
    }
    

    【讨论】:

      【解决方案3】:

      我在 infoView 中检查触摸是否发生的快速版本:

      override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
          guard let touch = touches.first, infoView.bounds.contains(touch.location(in: infoView))  else { return }
          print("touchesBegan") // Replace by your own code
      } // touchesBegan
      

      【讨论】:

        【解决方案4】:

        快速代码:

         override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
                if let touch = touches.first {
                    if touch.view == self.view {
                        self.hideInView()
                    } else {
                        return
                    }
        
                }
            }
        

        【讨论】:

        • Swift 的最佳答案。用赞成票分享爱。
        • 不适用于其他窗口的视图。例如使用附件视图。但是,这将起作用
        【解决方案5】:

        这对我有用:

        func respondToGesture(gesture: UIGestureRecognizer) {
            let containsPoint = CGRectContainsPoint(targetView.bounds, gesture.locationInView(targetView))
        }
        

        【讨论】:

          【解决方案6】:

          这也可以通过以下方式完成。

          首先在你关心的视图中找到触摸的位置:

          CGPoint location = [touches.anyObject locationInView:viewYouCareAbout];
          

          然后看看这个点是否在视图的范围内:

          BOOL withinBounds = CGRectContainsPoint(viewYouCareAbout.bounds, location);
          

          然后,如果它在界限内,请执行您的操作。

          这可以通过一个语句来完成,例如,可以直接在 if 语句中使用:

          CGRectContainsPoint(viewYouCareAbout.bounds, [touches.anyObject locationInView:viewYouCareAbout])
          

          【讨论】:

            【解决方案7】:

            在 touchesBegan 中检查触摸的视图是否是您想要的视图,当我尝试确定用户是否触摸了特定的 ImageView 时,为我解决了这个问题。 p>

            - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
            
                if ([touch view] ==  imageView ) {
                    ... your code to handle the touch
                }
                ...
            

            如果您调整视图尺寸的内容,请确保相应地更改视图大小,否则即使在视图中存在此类内容的区域,它也会识别触摸事件。在我的情况下,当我尝试使用 UIViewContentModeScaleAspectFit 保持图像比例时,尽管在“空白”区域触摸事件中也根据需要调整了图像。

            【讨论】:

              【解决方案8】:

              为了检查另一个视图中的某个视图是否被触摸,您可以使用 hitTest。

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

              在您自定义的 touchesBegan 实现中,检查 touches 集中的每个触摸。 hitTest 方法的点可以使用

              - (CGPoint)locationInView:(UIView *)view;
              

              方法,其中视图是您的超级视图(包含其他视图的视图)。

              编辑:这是一个快速的自定义实现:

              -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 
                  CGPoint locationPoint = [[touches anyObject] locationInView:self];
                  UIView* viewYouWishToObtain = [self hitTest:locationPoint withEvent:event];
              }
              

              希望对您有所帮助, 保罗

              【讨论】:

              • 抱歉,我知道您的思路是正确的,但我在理解您在说什么时遇到了一些麻烦。我从你的 locationInView 的触摸中得到了一点触动,我可以从技术上计算出用户从中触摸了哪个 uiview,但我不确定它是如何调用 hitTest
              • 需要注意的是,使用nil的参数调用locationInView:会得到CGPoint在主窗口坐标空间中的位置。
              猜你喜欢
              • 2011-11-13
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2016-04-24
              • 2013-03-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多