ios 如何禁用除最顶层视图之外的所有视图的触摸输入?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5404856/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 19:20:13  来源:igfitidea点击:

How to disable touch input to all views except the top-most view?

iphoneioscocoa-touchuiviewuser-interaction

提问by MusiGenesis

I have a view with multiple subviews. When a user taps a subview, the subview expands in size to cover most of the screen, but some of the other subviews are still visible underneath.

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

I want my app to ignore touches on the other subviews when one of the subviews is "expanded" like this. Is there a simple way to achieve this? I can write code to handle this, but I was hoping there's a simpler built-in way.

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

回答by Krishnabhadra

Hope this help...

希望这有助于...

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

which will disable userInteraction of a view's immediate subviews..Then give userInteraction to the only view you wanted

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

yourTouchableView.setUserInteraction = TRUE;

EDIT:

编辑:

It seems in iOS disabling userInteraction on a parent view doesn't disable userInteraction on its childs.. So the code above (I mean the one withmakeObjectsPerformSelector:)will only work to disable userInteraction of a parent's immediate subviews..

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

See user madewulf's answer which recursively get all subviews and disable user interaction of all of them. Or if you need to disable userInteraction of this view in many places in the project, You can categorize UIView to add that feature.. Something like this will do..

查看用户 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

Now you can call

现在你可以打电话

[yourSuperView setRecursiveUserInteraction:NO];

Also user @lxt's suggestion of adding an invisible view on top of all view's is one other way of doing it..

此外,用户@lxt 建议在所有视图之上添加一个不可见视图是另一种方法。

回答by lxt

There are a couple of ways of doing this. You could iterate through all your other subviews and set userInteractionEnabled = NO, but this is less than ideal if you have lots of other views (you would, after all, have to subsequently renable them all).

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

The way I do this is to create an invisible UIView that's the size of the entire screen that 'blocks' all the touches from going to the other views. Sometimes this is literally invisible, other times I may set it to black with an alpha value of 0.3 or so.

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

When you expand your main subview to fill the screen you can add this 'blocking' UIView behind it (using insertSubview: belowSubview:). When you minimize your expanded subview you can remove the invisible UIView from your hierarchy.

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

So not quite built-in, but I think the simplest approach. Not sure if that was what you were thinking of already, hopefully it was of some help.

所以不是很内置,但我认为是最简单的方法。不知道这是否是你已经想到的,希望它有一些帮助。

回答by madewulf

Beware of the code given as solution here by Krishnabhadra:

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

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

This will not work in all cases because [yourSuperView subviews] only gives the direct subviews of the superview. To make it work, you will have to iterate recursively on all subviews:

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

-(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];
    }
} 

Now a call to disableAllSubviewsOfwill do what you wanted to do.

现在调用disableAllSubviewsOf将做你想做的事。

If you have a deep stack of views, the solution by lxt is probably better.

如果您有很深的视图堆栈,lxt 的解决方案可能会更好。

回答by JPetric

I would do this by putting a custom transparent buttonwith the same frame as the superView. And then on top of that button I would put view that should accept user touches. Button will swallow all touchesand views behind it wouldn't receive any touch events, but view on top of the button will receive touches normally.

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

Something like this:

像这样的东西:

- (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];
}

And a method for enabling the parentView.

以及启用parentView.

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

So, to disable all views in the parentVievbehind yourView, I would do this:

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

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];

回答by musixlemon

Just parentView.UserInteractionEnabled = NOwill do the work. Parent view will disableuser interaction on all the view's subviews. But enableit does not enableall subviews(by default UIImageView is not interactable). So an easy way is find the parent view and use the code above, and there is no need to iterate all subviews to perform a selector.

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

回答by tianwalker

I will give my 2 cents to this problem. Iteratively run userInteractionEnabled = false it's one way. Another way will be add a UIView like following.

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

EZEventEater.h

EZEventEater.h

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

EZEventEater.m

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
{

}

In your code you add the EZEventEater view to cover all the views that your may block the touch event. Whenever you want to block the touch event to those views, simply call

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

eater.userInteractionEnabled = YES;

Hope this helpful.

希望这有帮助。

回答by Robert Atkins

Add a TapGestureRecognizer to your "background view" (the translucent one which "grays out" your normal interface) and set it to "Cancels Touches In View", without adding an action.

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

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

回答by Chris Prince

For my app, I think it will be sufficient to disable navigation to other tabs of the app (for a limited duration, while I'm doing some processing):

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

self.tabBarController.view.userInteractionEnabled = NO;

Also, I disabled the current view controller--

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

self.view.userInteractionEnabled = NO;

(And, by the way, the recursive solutions proposed here had odd effects in my app. The disable seems to work fine, but the re-enable has odd effects-- some of the UI was not renabled).

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

回答by Kirby Todd

setUserInteractionEnabled = NOon the view you want to disable

setUserInteractionEnabled = NO在您要禁用的视图上

回答by Werner Kratochwil

I had the same problem, but the above solutions did not help. I then noticed that calling super.touchesBegan(...)was the problem. After removing this the event was only handled by the top-most view.

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

I hope this is of help to anybody.

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