【发布时间】:2011-04-19 13:22:23
【问题描述】:
我有一个视图覆盖在许多其他视图之上。我只是使用 overaly 来检测屏幕上的一些触摸,但除此之外,我不希望视图停止下面其他视图的行为,例如滚动视图等。我怎样才能转发所有的触摸通过这个叠加视图?它是 UIView 的子类。
【问题讨论】:
-
您的问题解决了吗?如果是,那么请接受答案,以便其他人更容易找到解决方案,因为我面临同样的问题。
我有一个视图覆盖在许多其他视图之上。我只是使用 overaly 来检测屏幕上的一些触摸,但除此之外,我不希望视图停止下面其他视图的行为,例如滚动视图等。我怎样才能转发所有的触摸通过这个叠加视图?它是 UIView 的子类。
【问题讨论】:
我只需要禁用用户交互!
目标-C:
myWebView.userInteractionEnabled = NO;
斯威夫特:
myWebView.isUserInteractionEnabled = false
【讨论】:
myWebView.isUserInteractionEnabled = false
为了将触摸从覆盖视图传递到下面的视图,请在 UIView 中实现以下方法:
目标-C:
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
NSLog(@"Passing all touches to the next view (if any), in the view stack.");
return NO;
}
斯威夫特 5:
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
print("Passing all touches to the next view (if any), in the view stack.")
return false
}
【讨论】:
这是一个旧线程,但它出现在搜索中,所以我想我会添加我的 2c。我有一个带有子视图的覆盖 UIView,并且只想拦截触及其中一个子视图的触摸,所以我修改了 PixelCloudSt 的答案
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
for (UIView* subview in self.subviews ) {
if ( [subview hitTest:[self convertPoint:point toView:subview] withEvent:event] != nil ) {
return YES;
}
}
return NO;
}
【讨论】:
@fresidue 答案的改进版本。您可以将此UIView 子类用作透明视图,将触摸传递到其子视图之外。 Objective-C 中的实现:
@interface PassthroughView : UIView
@end
@implementation PassthroughView
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
for (UIView *view in self.subviews) {
if (!view.hidden && [view pointInside:[self convertPoint:point toView:view] withEvent:event]) {
return YES;
}
}
return NO;
}
@end
.
在 Swift 中:
class PassthroughView: UIView {
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
return subviews.contains(where: {
!$0.isHidden
&& $0.isUserInteractionEnabled
&& $0.point(inside: self.convert(point, to: $0), with: event)
})
}
}
假设你有一个大的“支架”面板,后面可能有一个表格视图。您制作了“持有人”面板PassthroughView。它现在可以工作了,您可以“通过”“持有人”滚动表格。
但是!
在“支架”面板的顶部有一些标签或图标。别忘了,当然这些必须简单地标记为用户交互启用关闭!
在“持有人”面板的顶部有一些按钮。不要忘记,当然这些必须简单地标记为启用用户交互!
注意有点令人困惑的是,“持有人”本身——您在上面使用PassthroughView 的视图——必须标记为启用用户交互ON!那是开启!! (否则,PassthroughView 中的代码将永远不会被调用。)
【讨论】:
$0.isUserInteractionEnabled
我需要通过 UIStackView 传递触摸。里面的 UIView 是透明的,但 UIStackView 消耗了所有的触摸。这对我有用:
class PassThrouStackView: UIStackView {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let view = super.hitTest(point, with: event)
if view == self {
return nil
}
return view
}
}
所有排列的子视图仍会收到触摸,但 UIStackView 本身的触摸会传递到下面的视图(对我来说是 mapView)。
【讨论】:
我在 UIStackView 上遇到了类似的问题(但可能是任何其他视图)。 我的配置如下:
这是一个经典案例,我有一个需要放置在背景中的容器,侧面有按钮。出于布局目的,我将按钮包含在 UIStackView 中,但现在 stackView 的中间(空)部分拦截了触摸:-(
我所做的是创建 UIStackView 的子类,其中包含定义应该可触摸的子视图的属性。 现在,对侧边按钮(包含在 *viewsWithActiveTouch* 数组中)的任何触摸都将被赋予按钮,而对除了这些视图之外的任何位置的堆栈视图的任何触摸都不会被拦截,因此将传递给堆栈下方的任何内容查看。
/** Subclass of UIStackView that does not accept touches, except for specific subviews given in the viewsWithActiveTouch array */
class NoTouchStackView: UIStackView {
var viewsWithActiveTouch: [UIView]?
override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
if let activeViews = viewsWithActiveTouch {
for view in activeViews {
if CGRectContainsPoint(view.frame, point) {
return view
}
}
}
return nil
}
}
【讨论】:
如果你想转发触摸的视图不是子视图/超级视图,你可以像这样在你的 UIView 子类中设置一个自定义属性:
@interface SomeViewSubclass : UIView {
id forwardableTouchee;
}
@property (retain) id forwardableTouchee;
确保在你的 .m 中合成它:
@synthesize forwardableTouchee;
然后在您的任何 UIResponder 方法中包含以下内容,例如:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self.forwardableTouchee touchesBegan:touches withEvent:event];
}
无论您在何处实例化您的 UIView,请将 forwardableTouchee 属性设置为您希望将事件转发到的任何视图:
SomeViewSubclass *view = [[[SomeViewSubclass alloc] initWithFrame:someRect] autorelease];
view.forwardableTouchee = someOtherView;
【讨论】:
在 Swift 5 中
class ThroughView: UIView {
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
guard let slideView = subviews.first else {
return false
}
return slideView.hitTest(convert(point, to: slideView), with: event) != nil
}
}
【讨论】:
看起来即使你在这里也有很多答案,没有一个我需要快速干净的人。 所以我在这里从@fresidue 那里得到了答案,并将其转换为 swift,因为它是现在大多数开发人员想要在这里使用的。
它解决了我的问题,我有一些带有按钮的透明工具栏,但我希望工具栏对用户不可见,并且触摸事件应该通过。
isUserInteractionEnabled = false 正如某些人所说,根据我的测试,这不是一个选项。
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
for subview in subviews {
if subview.hitTest(convert(point, to: subview), with: event) != nil {
return true
}
}
return false
}
【讨论】:
我在 StackView 中有几个标签,但上面的解决方案并没有取得多大成功,而是使用以下代码解决了我的问题:
let item = ResponsiveLabel()
// Configure label
stackView.addArrangedSubview(item)
子类化 UIStackView:
class PassThrouStackView:UIStackView{
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
for subview in self.arrangedSubviews {
let convertedPoint = convert(point, to: subview)
let labelPoint = subview.point(inside: convertedPoint, with: event)
if (labelPoint){
return subview
}
}
return nil
}
}
然后你可以这样做:
class ResponsiveLabel:UILabel{
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
// Respond to touch
}
}
【讨论】:
试试这样的...
for (UIView *view in subviews)
[view touchesBegan:touches withEvent:event];
上面的代码,例如在您的 touchesBegan 方法中,会将触摸传递给视图的所有子视图。
【讨论】:
我试图做的情况是使用嵌套 UIStackView 中的控件构建一个控制面板。一些控件具有 UITextField 的其他控件具有 UIButton 的。此外,还有用于识别控件的标签。我想做的是在控制面板后面放一个“不可见”的大按钮,这样如果用户点击按钮或文本字段之外的区域,我就可以捕捉到并采取行动——主要是在出现文本时关闭任何键盘字段处于活动状态(resignFirstResponder)。但是,点击控制面板中的标签或其他空白区域不会通过。上述讨论有助于我在下面给出答案。
基本上,我将 UIStackView 子类化并重写了“point(inside:with)”例程,以查找需要触摸的控件类型,并“忽略”我想忽略的标签之类的东西。它还检查 UIStackView 内部,以便可以递归到控制面板结构中。
代码可能比它应该的更冗长。但它对调试很有帮助,并希望能更清楚地说明例程正在做什么。只需确保在 Interface Builder 中将 UIStackView 的类更改为 PassThruStack。
class PassThruStack: UIStackView {
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
for view in self.subviews {
if !view.isHidden {
let isStack = view is UIStackView
let isButton = view is UIButton
let isText = view is UITextField
if isStack || isButton || isText {
let pointInside = view.point(inside: self.convert(point, to: view), with: event)
if pointInside {
return true
}
}
}
}
return false
}
}
【讨论】:
正如@PixelCloudStv 所建议的,如果您想将触摸从一个视图投射到另一个视图,但对这个过程有一些额外的控制 - 子类 UIView
//标题
@interface TouchView : UIView
@property (assign, nonatomic) CGRect activeRect;
@end
//实现
#import "TouchView.h"
@implementation TouchView
#pragma mark - Ovverride
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
BOOL moveTouch = YES;
if (CGRectContainsPoint(self.activeRect, point)) {
moveTouch = NO;
}
return moveTouch;
}
@end
在 interfaceBuilder 中,只需将 View 类设置为 TouchView 并使用您的矩形设置活动矩形。你也可以改变和实现其他逻辑。
【讨论】: