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

发布于 2024-10-25 13:03:21 字数 161 浏览 7 评论 0原文

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

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

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

一梦等七年七年为一梦 2024-11-01 13:03:21

希望这个帮助...

[[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的建议,在顶部添加不可见视图所有视图的另一种方式是这样做的..

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

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 with makeObjectsPerformSelector:)will only work to disable userInteraction of a parent's immediate subviews..

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..

@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..

终难愈 2024-11-01 13:03:21

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

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

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

所以不太内置,但我认为最简单的方法。不知道您是否已经想到了这一点,希望对您有所帮助。

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).

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.

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.

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.

因为看清所以看轻 2024-11-01 13:03:21

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

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

这并非在所有情况下都有效,因为 [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];
    }
} 

现在调用 disableAllSubviewsOf 将执行您想要执行的操作。

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

Beware of the code given as solution here by 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:

-(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 disableAllSubviewsOf will do what you wanted to do.

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

冷夜 2024-11-01 13:03:21

我会通过放置一个与 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];

I would do this by putting a custom transparent button with 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 touches and views behind it wouldn't receive any touch events, but view on top of the button will receive touches normally.

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.

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

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

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];
旧伤慢歌 2024-11-01 13:03:21

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

Just parentView.UserInteractionEnabled = NO will do the work.
Parent view will disable user interaction on all the view's subviews. But enable it does not enable all 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.

风为裳 2024-11-01 13:03:21

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

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

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.

let captureTaps = UITapGestureRecognizer()
captureTaps.cancelsTouchesInView = true
dimmedOverlay?.addGestureRecognizer(captureTaps)
你的心境我的脸 2024-11-01 13:03:21

我将为这个问题付出 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;

希望这有帮助即可。

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.

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
{

}

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

eater.userInteractionEnabled = YES;

Hope this helpful.

画尸师 2024-11-01 13:03:21

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

myView.isUserInteractionEnabled = true

这不会让触摸穿过它,从而忽略点击,从而实现了此行为。

输入图片此处描述

In Swift 5, I achieved this behaviour by placing a view right on top(the highlighted one) and setting:

myView.isUserInteractionEnabled = true

This does not let the touches go through it, thus ignoring the taps.

enter image description here

生生不灭 2024-11-01 13:03:21

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

self.tabBarController.view.userInteractionEnabled = NO;

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

self.view.userInteractionEnabled = NO;

(并且,顺便说一句,此处提出的递归解决方案在我的应用程序中产生了奇怪的效果,禁用似乎工作正常,但重新启用却产生了奇怪的效果——某些 UI 未重新启用。

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).

你又不是我 2024-11-01 13:03:21

简单的解决方案。添加一个不执行任何操作的虚拟手势。通过将其添加到扩展中,使其可重用,如下所示:

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

Simple solution. Add a dummy gesture that does nothing. Make it reusable by adding it to an extension like this:

extension UIView {
    func addNullGesture() {
        let gesture = UITapGestureRecognizer(target: self,
                                             action: #selector(nullGesture))
        addGestureRecognizer(gesture)
    }
    
    @objc private func nullGesture() {}
}
池木 2024-11-01 13:03:21

在要禁用的视图上 setUserInteractionEnabled = NO

setUserInteractionEnabled = NO on the view you want to disable

帝王念 2024-11-01 13:03:21

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

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

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.

I hope this is of help to anybody.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文