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

时间:2010-09-30 19:54:11

标签: events uiview touch

我有一个视图叠加在许多其他视图之上。我只是使用过度的方法来检测屏幕上的一些触摸,但除此之外我不希望视图停止下面其他视图的行为,这些是滚动视图等。我怎样才能转发所有的触摸通过这个叠加视图?这是UIView的一个主要内容。

14 个答案:

答案 0 :(得分:101)

要将覆盖视图中的触摸传递到下面的视图,请在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;
}

夫特:

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
}

答案 1 :(得分:89)

myWebView.userInteractionEnabled = NO;

就是我所需要的一切!

答案 2 :(得分:50)

这是一个老线程,但它出现在搜索中,所以我想我会添加我的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;
}

答案 3 :(得分:29)

@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.point(inside: self.convert(point, to: $0), with: event)
    })
  }
}

答案 4 :(得分:6)

我需要通过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
    }
}

所有arrangedSubviews仍会收到触摸,但是对UIStackView本身的触摸进入了下面的视图(对我来说是mapView)。

答案 5 :(得分:5)

如果要转发的视图不是子视图/超级视图,则可以在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;

答案 6 :(得分:5)

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

这是一个经典案例,我有一个需要放在背景中的容器,侧面有按钮。出于布局目的,我在UIStackView中包含了按钮,但现在stackView的中间(空)部分拦截了触摸: - (

我所做的是创建一个UIStackView的子类,其中一个属性定义了应该可触摸的subView。 现在,对侧按钮(包含在* 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
   }
}

答案 7 :(得分:0)

尝试这样的事情......

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

上面的代码,例如在你的touchesBegan方法中,会将触摸传递给所有视图的子视图。

答案 8 :(得分:0)

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

答案 9 :(得分:0)

即使您在这里看起来也有很多答案,但我所需要的并没有那么迅速。 所以我从这里的@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
}

答案 10 :(得分:0)

我在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
    }
}

答案 11 :(得分:0)

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

}

答案 12 :(得分:-1)

正如@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设置活动rect。你也可以改变并实现其他逻辑。

答案 13 :(得分:-3)

Swift 4.0 及更高版本中进行的更新对我来说很有效:

myWebView.isUserInteractionEnabled = false;