我怎样才能点击透明UIView背后的button?
假设我们有一个视图控制器和一个子视图。 子视图占据屏幕的中心,所有边都有100px的边距。 然后我们添加一些小东西来点击子视图内。 我们只使用子视图来利用新的框架(x = 0,在子视图中y = 0实际上是100,100在父视图中)。
然后,想象我们在子视图背后有什么东西,比如菜单。 我希望用户能够select子视图中的任何“小东西”,但是如果没有任何东西,我想触摸它(因为背景是清楚的)到它后面的button。
我怎样才能做到这一点? 它看起来像touchesBegan通过,但button不起作用。
为你的容器创build一个自定义的视图,并重写pointInside:消息,当点不在一个合格的子视图内时,返回NO,如下所示:
@interface PassthroughView : UIView @end @implementation PassthroughView -(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { for (UIView *view in self.subviews) { if (!view.hidden && view.userInteractionEnabled && [view pointInside:[self convertPoint:point toView:view] withEvent:event]) return YES; } return NO; } @end
使用这个视图作为容器将允许它的任何孩子接触,但是视图本身对事件是透明的。
编辑:这是Swift版本
class PassThroughView: UIView { override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool { for subview in subviews { if !subview.hidden && subview.userInteractionEnabled && subview.pointInside(convertPoint(point, toView: subview), withEvent: event) { return true } } return false } }
Swift 3:
class PassThroughView: UIView { override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { for subview in subviews { if !subview.isHidden && subview.isUserInteractionEnabled && subview.point(inside: convert(point, to: subview), with: event) { return true } } return false } }
我也用
myView.userInteractionEnabled = NO;
不需要子类。 工作正常。
从苹果:
事件转发是一些应用程序使用的技术。 您通过调用另一个响应者对象的事件处理方法来转发触摸事件。 虽然这可能是一种有效的技术,但应谨慎使用。 UIKit框架的类没有被devise为接收不与它们绑定的触摸….如果你想有条件地将触摸转发给应用程序中的其他响应者,所有这些响应者应该是你自己的UIView的子类的实例。
苹果最佳实践 :
不要明确地向响应者链发送事件(通过nextResponder); 相反,调用超类实现并让UIKit处理响应者链遍历。
相反,你可以覆盖:
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
在你的UIView子类中,如果你想让这个触摸被发送到响应者链(IE浏览器在没有任何东西的情况下查看你的视图后面),则返回NO。
最近我写了一个能够帮助我的课程。 将它用作UIButton
或UIView
的自定义类将传递在透明像素上执行的触摸事件。
这个解决scheme比接受的答案要好一些,因为你仍然可以点击一个半透明UIView
下的UIButton
,而UIView
的非透明部分仍然会响应触摸事件。
正如你在GIF中看到的,长颈鹿button是一个简单的矩形,但是透明区域上的触摸事件被传递到下面的黄色UIButton
。
链接到课堂
基于John发布的内容,下面是一个例子,它允许触摸事件通过视图的所有子视图,除了button:
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { // Allow buttons to receive press events. All other views will get ignored for( id foundView in self.subviews ) { if( [foundView isKindOfClass:[UIButton class]] ) { UIButton *foundButton = foundView; if( foundButton.isEnabled && !foundButton.hidden && [foundButton pointInside:[self convertPoint:point toView:foundButton] withEvent:event] ) return YES; } } return NO; }
一个更简单的方法是在界面构build器中启用“Un-Check”User Interaction。 “如果你使用故事板”
我创build了一个类别来做到这一点。
一个小小的方法搅拌,并认为是金色的。
标题
//UIView+PassthroughParent.h @interface UIView (PassthroughParent) - (BOOL) passthroughParent; - (void) setPassthroughParent:(BOOL) passthroughParent; @end
执行文件
#import "UIView+PassthroughParent.h" @implementation UIView (PassthroughParent) + (void)load{ Swizz([UIView class], @selector(pointInside:withEvent:), @selector(passthroughPointInside:withEvent:)); } - (BOOL)passthroughParent{ NSNumber *passthrough = [self propertyValueForKey:@"passthroughParent"]; if (passthrough) return passthrough.boolValue; return NO; } - (void)setPassthroughParent:(BOOL)passthroughParent{ [self setPropertyValue:[NSNumber numberWithBool:passthroughParent] forKey:@"passthroughParent"]; } - (BOOL)passthroughPointInside:(CGPoint)point withEvent:(UIEvent *)event{ // Allow buttons to receive press events. All other views will get ignored if (self.passthroughParent){ if (self.alpha != 0 && !self.isHidden){ for( id foundView in self.subviews ) { if ([foundView alpha] != 0 && ![foundView isHidden] && [foundView pointInside:[self convertPoint:point toView:foundView] withEvent:event]) return YES; } } return NO; } else { return [self passthroughPointInside:point withEvent:event];// Swizzled } } @end
你将需要添加我的Swizz.h和Swizz.m
位于这里
之后,您只需在{Project} -Prefix.pch文件中导入UIView + PassthroughParent.h,并且每个视图都具有此function。
每一个观点都需要点,但没有一个空白的地方。
我也build议使用清晰的背景。
myView.passthroughParent = YES; myView.backgroundColor = [UIColor clearColor];
编辑
我创造了我自己的财产袋,这是不包括以前。
头文件
// NSObject+PropertyBag.h #import <Foundation/Foundation.h> @interface NSObject (PropertyBag) - (id) propertyValueForKey:(NSString*) key; - (void) setPropertyValue:(id) value forKey:(NSString*) key; @end
实施文件
// NSObject+PropertyBag.m #import "NSObject+PropertyBag.h" @implementation NSObject (PropertyBag) + (void) load{ [self loadPropertyBag]; } + (void) loadPropertyBag{ @autoreleasepool { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ Swizz([NSObject class], NSSelectorFromString(@"dealloc"), @selector(propertyBagDealloc)); }); } } __strong NSMutableDictionary *_propertyBagHolder; // Properties for every class will go in this property bag - (id) propertyValueForKey:(NSString*) key{ return [[self propertyBag] valueForKey:key]; } - (void) setPropertyValue:(id) value forKey:(NSString*) key{ [[self propertyBag] setValue:value forKey:key]; } - (NSMutableDictionary*) propertyBag{ if (_propertyBagHolder == nil) _propertyBagHolder = [[NSMutableDictionary alloc] initWithCapacity:100]; NSMutableDictionary *propBag = [_propertyBagHolder valueForKey:[[NSString alloc] initWithFormat:@"%p",self]]; if (propBag == nil){ propBag = [NSMutableDictionary dictionary]; [self setPropertyBag:propBag]; } return propBag; } - (void) setPropertyBag:(NSDictionary*) propertyBag{ if (_propertyBagHolder == nil) _propertyBagHolder = [[NSMutableDictionary alloc] initWithCapacity:100]; [_propertyBagHolder setValue:propertyBag forKey:[[NSString alloc] initWithFormat:@"%p",self]]; } - (void)propertyBagDealloc{ [self setPropertyBag:nil]; [self propertyBagDealloc];//Swizzled } @end
根据“iPhone应用程序编程指南”:
closures触摸事件的传送。 默认情况下,视图接收触摸事件,但是您可以将其userInteractionEnabled属性设置为NO,以closures事件的传递。 视图如果隐藏或透明,也不会收到事件 。
更新 :删除的例子 – 重读这个问题…
你有任何手势处理的意见,可能正在处理水龙头之前,button得到它? 当你没有透明的视图时,button是否工作?
任何代码示例的非工作代码?
从其他答案中获取技巧并阅读苹果的文档,我创build了这个简单的库来解决您的问题:
https://github.com/natrosoft/NATouchThroughView
它使得在Interface Builder中绘制视图的过程变得很容易,这些视图应该通过底层视图来传递。
我认为在生产代码中做方法调整是过度的,也是非常危险的,因为你直接搞乱了苹果的基础实现,并在应用程序范围内进行改变,这可能会导致意想不到的后果。
有一个演示项目,希望README能够很好地解释要做什么。 为了解决OP问题,你可以在界面构build器中将包含button的清晰的UIView更改为类NATouchThroughView。 然后find覆盖菜单的清晰的UIView,你想要点击。 将该UIView更改为Interface Builder中的类NARootTouchThroughView。 它甚至可以是你的视图控制器的根UIView,如果你打算将这些触摸传递给底层的视图控制器。 看看演示项目,看看它是如何工作的。 这真的很简单,安全,无创
据我所知,你应该能够通过重写hitTest:方法来做到这一点。 我尝试过,但无法正常工作。
最后,我在可触摸对象周围创build了一系列透明的视图,以便它们不覆盖它。 我的问题,这个工作正常的黑客的位。
如果你不能打扰使用UIView的类或子类,你也可以将button向前移动,使它位于透明视图的前面。 这并不总是可能的取决于你的应用程序,但它为我工作。 您可以随时将button重新放回或隐藏。