【问题标题】:How to disable touch input to all views except the top-most view?如何禁用除最顶层视图之外的所有视图的触摸输入?
【发布时间】:2011-07-21 06:19:33
【问题描述】:

我有一个包含多个子视图的视图。当用户点击子视图时,子视图的大小会扩大以覆盖大部分屏幕,但其他一些子视图仍然在下方可见。

当其中一个子视图像这样“展开”时,我希望我的应用忽略对其他子视图的触摸。有没有一种简单的方法来实现这一点?我可以编写代码来处理这个问题,但我希望有一个更简单的内置方法。

【问题讨论】:

    标签: iphone ios cocoa-touch uiview user-interaction


    【解决方案1】:

    希望对您有所帮助...

    [[yourSuperView subviews]
       makeObjectsPerformSelector:@selector(setUserInteractionEnabled:)
       withObject:[NSNumber numberWithBool:FALSE]];
    

    这将禁用视图的直接子视图的 userInteraction..然后将 userInteraction 提供给您想要的唯一视图

    yourTouchableView.setUserInteraction = TRUE;
    

    编辑:

    似乎在 iOS 中禁用父视图上的 userInteraction 并不会禁用其子视图上的 userInteraction。所以上面的代码(我的意思是带有 makeObjectsPerformSelector: 的代码)只会工作禁用父级直接子视图的 userInteraction..

    查看用户 madewulf 的答案,它递归地获取所有子视图并禁用所有子视图的用户交互。或者如果您需要在项目中的许多地方禁用此视图的 userInteraction,您可以对 UIView 进行分类以添加该功能.. 这样的事情就可以了..

    @interface UIView (UserInteractionFeatures)
    -(void)setRecursiveUserInteraction:(BOOL)value;
    @end
    
    @implementation UIView(UserInteractionFeatures)
    -(void)setRecursiveUserInteraction:(BOOL)value{
        self.userInteractionEnabled =   value;
        for (UIView *view in [self subviews]) {
            [view setRecursiveUserInteraction:value];
        }
    }
    @end
    

    现在你可以打电话了

    [yourSuperView setRecursiveUserInteraction:NO];
    

    另外,用户@lxt 建议在所有视图之上添加一个不可见视图是另一种实现方式..

    【讨论】:

    • 我一定是做错了什么。我将这一行放在我的视图控制器中:[[self.view subviews] makeObjectsPerformSelector:@selector(setUserInteractionEnabled:FALSE)]; 并且在数字常量之前出现编译错误“Expected ')'。
    • 好吧,它编译并运行,但似乎没有任何效果——我仍然可以点击任何子视图。我实际上在每个视图的视图控制器中都收到了 touchesBegan 事件 - 这可能是为什么这不起作用吗?
    • 它肯定正在执行,而且它肯定不会禁用我的任何子视图上的用户交互。
    • 我不知道它是否有效,但它是你那里的一段非常好的编码......在一行中完成所有这些,真的很好...... :)
    • 事实并非如此,我测试了它,它可能解释了为什么上面的一些人报告说这个解决方案不适合他们。
    【解决方案2】:

    有几种方法可以做到这一点。您可以遍历所有其他子视图并设置userInteractionEnabled = NO,但如果您有很多其他视图(毕竟,您随后必须重新启用它们),这不太理想。

    我这样做的方法是创建一个不可见的 UIView,它是整个屏幕的大小,它“阻止”所有触摸进入其他视图。有时这实际上是不可见的,有时我可能会将其设置为黑色,alpha 值为 0.3 左右。

    当您展开主子视图以填满屏幕时,您可以在其后面添加这个“阻塞”UIView(使用insertSubview: belowSubview:)。当您最小化展开的子视图时,您可以从层次结构中删除不可见的 UIView。

    所以不是完全内置,但我认为是最简单的方法。不确定这是否是您已经想到的,希望对您有所帮助。

    【讨论】:

    • 你将如何完成这个?
    • 你会通过添加手势识别器来“阻止”对吗?
    • 没问题,很高兴我能帮上忙!
    • 你实际上是如何“阻止”触摸的?那是我不明白的部分。
    • 您可以通过添加自定义不可见按钮而不是简单的 UIView 来阻止触摸。详情请查看我的回答
    【解决方案3】:

    请注意 Krishnabhadra 在此处作为解决方案给出的代码:

    [[yourSuperView subviews]makeObjectsPerformSelector:@selector(setUserInteractionEnabled:) withObject:[NSNumber numberWithBool:FALSE]];
    

    这不会在所有情况下都有效,因为 [yourSuperView subviews] 只给出了 superview 的直接子视图。为了让它工作,你必须递归地迭代所有的子视图:

    -(void) disableRecursivelyAllSubviews:(UIView *) theView
    {
        theView.userInteractionEnabled = NO;
        for(UIView* subview in [theView subviews])
        {
            [self disableRecursivelyAllSubviews:subview];
        }
    }
    
    -(void) disableAllSubviewsOf:(UIView *) theView
    {
        for(UIView* subview in [theView subviews])
        {
            [self disableRecursivelyAllSubviews:subview];
        }
    } 
    

    现在拨打disableAllSubviewsOf 将完成您想做的事情。

    如果您有大量视图,lxt 的解决方案可能会更好。

    【讨论】:

      【解决方案4】:

      我会通过放置一个与superView 具有相同框架的自定义透明按钮来做到这一点。然后在那个按钮的顶部,我会放置应该接受用户触摸的视图。 按钮会吞噬所有的触摸,它后面的视图不会接收到任何触摸事件,但按钮顶部的视图会正常接收触摸。

      类似这样的:

      - (void)disableTouchesOnView:(UIView *)view {
          UIButton *ghostButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, view.frame.size.width, view.frame.size.height)];
          [ghostButton setBackgroundColor:[UIColor clearColor]];
          ghostButton.tag = 42; // Any random number. Use #define to avoid putting numbers in code.
      
          [view addSubview:ghostButton];
      }
      

      还有一种启用parentView的方法。

      - (void)enableTouchesOnView:(UIView *)view {
          [[view viewWithTag:42] removeFromSuperview];
      }
      

      所以,要禁用yourView 后面的parentViev 中的所有视图,我会这样做:

      YourView *yourView = [[YourView alloc] initWithCustomInitializer];
      // It is important to disable touches on the parent view before adding the top most view.
      [self disableTouchesOnView:parentView];
      [parentView addSubview:yourView];
      

      【讨论】:

      • 谢谢,好简单的方法。我还添加了一个 alpha = 0.25 来制作一个漂亮的透明幻影视图。 ghostButton.alpha = 0.25;
      【解决方案5】:

      只需parentView.UserInteractionEnabled = NO 即可完成这项工作。 父视图将在所有视图的子视图上禁用用户交互。但是 enable 它不会 enable 所有子视图(默认情况下 UIImageView 不可交互)。所以一个简单的方法是找到父视图并使用上面的代码,并且不需要遍历所有子视图来执行选择器。

      【讨论】:

        【解决方案6】:

        将 TapGestureRecognizer 添加到您的“背景视图”(使您的正常界面“变灰”的半透明视图)并将其设置为“取消在视图中触摸”,而不添加任何操作。

        let captureTaps = UITapGestureRecognizer()
        captureTaps.cancelsTouchesInView = true
        dimmedOverlay?.addGestureRecognizer(captureTaps)
        

        【讨论】:

        • 更好的解决方案,遍历所有视图以取消他们的用户交互
        【解决方案7】:

        我会给这个问题我的 2 美分。 迭代运行 userInteractionEnabled = false 这是一种方法。 另一种方法是添加如下 UIView。

        EZEventEater.h

        #import <UIKit/UIKit.h>
        @interface EZEventEater : UIView
        @end
        

        EZEventEater.m

        #import "EZEventEater.h"
        @implementation EZEventEater
        
        - (id)initWithFrame:(CGRect)frame
        {
            self = [super initWithFrame:frame];
            if (self) {
                // Initialization code
                self.backgroundColor = [UIColor clearColor];
                self.userInteractionEnabled = false;
            }
            return self;
        }
        
        
        - (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
        {
           //EZDEBUG(@"eater touched");
        }
        
        - (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
        {
        
        }
        
        - (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
        {
        
        }
        

        在您的代码中添加 EZEventEater 视图以覆盖您可能阻止触摸事件的所有视图。 每当您想阻止这些视图的触摸事件时,只需调用

        eater.userInteractionEnabled = YES;
        

        希望这有帮助。

        【讨论】:

        • 这里的错字:self.userInteractionEnabled = true;因为如果设置为 false,则该视图不接受用户交互;从而将用户交互传递给原始 uiview。
        【解决方案8】:

        setUserInteractionEnabled = NO 在要禁用的视图上

        【讨论】:

        • 这将禁用他试图捕捉触摸的子视图中的交互。
        【解决方案9】:

        对于我的应用,我认为禁用导航到应用的其他选项卡就足够了(在有限的时间内,我正在做一些处理):

        self.tabBarController.view.userInteractionEnabled = NO;
        

        另外,我禁用了当前视图控制器--

        self.view.userInteractionEnabled = NO;
        

        (顺便说一下,这里提出的递归解决方案在我的应用程序中产生了奇怪的效果。禁用似乎工作正常,但重新启用有奇怪的效果 - 一些 UI 没有重新启用)。

        【讨论】:

          【解决方案10】:

          简单的解决方案。添加一个什么都不做的虚拟手势。通过将其添加到这样的扩展中使其可重用:

          extension UIView {
              func addNullGesture() {
                  let gesture = UITapGestureRecognizer(target: self,
                                                       action: #selector(nullGesture))
                  addGestureRecognizer(gesture)
              }
              
              @objc private func nullGesture() {}
          }
          

          【讨论】:

            【解决方案11】:

            我遇到了同样的问题,但上述解决方案没有帮助。 然后我注意到调用 super.touchesBegan(...) 是问题所在。 删除此事件后,该事件仅由最顶层视图处理。

            我希望这对任何人都有帮助。

            【讨论】:

              【解决方案12】:

              在 Swift 5 中,我通过将视图放在顶部(突出显示的视图)并设置:

              myView.isUserInteractionEnabled = true
              

              这不会让触摸通过它,因此忽略了点击。

              【讨论】:

                猜你喜欢
                • 2011-07-22
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2011-12-22
                • 1970-01-01
                相关资源
                最近更新 更多