IOS-通过视图转发所有触摸

我有一个视图叠加在许多其他视图之上。我只是用它来检测屏幕上的一些触摸,但是除此之外,我不希望视图阻止其他视图的行为,比如滚动视图等等。我如何通过这个覆盖视图转发所有的触摸?它是 UIView 的一个子类。

96929 次浏览

试试这样..。

for (UIView *view in subviews)
[view touchesBegan:touches withEvent:event];

以上代码为例,在您的 touch esBegan 方法中,它会将触摸传递给视图的所有子视图。

如果你想转发的视图不是 subview/superview,你可以在你的 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;

为了从覆盖视图向下面的视图传递触感,在 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
}

禁用用户交互就是我所需要的全部!

目标 C:

myWebView.userInteractionEnabled = NO;

斯威夫特:

myWebView.isUserInteractionEnabled = 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;
}

正如@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,并用 rect 设置 active rect。也可以更改和实现其他逻辑。

我在使用 UIStackView 时也遇到过类似的问题(但可能是其他任何视图)。 我的配置如下: View controller with containerView and StackView

这是一个经典的例子,我有一个容器,需要放置在背景中,侧面有按钮。出于布局的考虑,我在 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
}
}

@ fresidue 答案的改进版本。您可以使用这个 UIView子类作为透明视图,将触摸传递到其子视图之外。目标 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

.

以及 斯威夫特:

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

提示:

假设您有一个大的“ holder”面板,后面可能有一个表格视图。你使“持有人”面板 PassthroughView。它现在将工作,你可以滚动表“通过”的“持有人”。

但是!

  1. 在“ Holder”面板的顶部有一些标签或图标。不要忘记,当然那些必须简单地标记用户交互启用关闭!

  2. 在“ Holder”面板的顶部有一些按钮。不要忘记,当然,这些必须简单地标记用户交互启用 ON!

  3. 注意 ,有些令人困惑的是,“ holder”本身-您使用 PassthroughView的视图-必须标记为启用了 开始的用户交互!那是 开始! !(否则,Passthrough View 中的代码将永远不会被调用。)

在 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 这里获得了答案,并将其转换为现在大多数开发人员希望在这里使用的速度。

它解决了我的问题,我有一些透明的按钮工具栏,但我希望工具栏是不可见的用户和触摸事件应通过。

根据我的测试,isUserInteractionEnable = 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
}
}

我试图做的情况是使用嵌套的 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
}

}

我需要通过一个 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)。